00001 #include <unistd.h>
00002 #include <qpointarray.h>
00003 #include <qbitarray.h>
00004
00005 #include "mhi.h"
00006 #include "osd.h"
00007
00008 static bool ft_loaded = false;
00009 static FT_Library ft_library;
00010
00011 #define FONT_WIDTHRES 48
00012 #define FONT_HEIGHTRES 72
00013 #define FONT_TO_USE "FreeSans.ttf"
00014
00018 class MHIImageData
00019 {
00020 public:
00021 QImage m_image;
00022 int m_x;
00023 int m_y;
00024 };
00025
00026
00027 #define NBI_VERSION_UNSET 257
00028 #define NBI_VERSION_ABSENT 256
00029
00030 MHIContext::MHIContext(InteractiveTV *parent)
00031 : m_parent(parent), m_dsmcc(NULL),
00032 m_keyProfile(0),
00033 m_engine(NULL), m_stop(false),
00034 m_stopped(false), m_updated(false),
00035 m_displayWidth(StdDisplayWidth), m_displayHeight(StdDisplayHeight),
00036 m_face_loaded(false), m_currentChannel(-1),
00037 m_isLive(false), m_currentCard(0),
00038 m_audioTag(-1), m_videoTag(-1),
00039 m_tuningTo(-1), m_lastNbiVersion(NBI_VERSION_UNSET),
00040 m_videoRect(0, 0, StdDisplayWidth, StdDisplayHeight)
00041 {
00042 m_display.setAutoDelete(true);
00043 m_dsmccQueue.setAutoDelete(true);
00044
00045 if (!ft_loaded)
00046 {
00047 FT_Error error = FT_Init_FreeType(&ft_library);
00048 if (!error)
00049 ft_loaded = true;
00050 }
00051
00052 if (ft_loaded)
00053 {
00054
00055 if (LoadFont(FONT_TO_USE))
00056 m_face_loaded = true;
00057 }
00058 }
00059
00060
00061 bool MHIContext::LoadFont(QString name)
00062 {
00063 QString fullname = MythContext::GetConfDir() + "/" + name;
00064 FT_Error error = FT_New_Face(ft_library, fullname.ascii(), 0, &m_face);
00065 if (!error)
00066 return true;
00067
00068 fullname = gContext->GetShareDir() + name;
00069 error = FT_New_Face(ft_library, fullname.ascii(), 0, &m_face);
00070 if (!error)
00071 return true;
00072
00073 fullname = gContext->GetShareDir() + "themes/" + name;
00074 error = FT_New_Face(ft_library, fullname.ascii(), 0, &m_face);
00075 if (!error)
00076 return true;
00077
00078 fullname = name;
00079 error = FT_New_Face(ft_library, fullname.ascii(), 0, &m_face);
00080 if (!error)
00081 return true;
00082
00083 VERBOSE(VB_IMPORTANT, QString("Unable to find font: %1").arg(name));
00084 return false;
00085 }
00086
00087 MHIContext::~MHIContext()
00088 {
00089 StopEngine();
00090 delete(m_engine);
00091 delete(m_dsmcc);
00092 if (m_face_loaded) FT_Done_Face(m_face);
00093 }
00094
00095
00096 void MHIContext::StopEngine()
00097 {
00098 if (m_engine)
00099 {
00100 while (!m_stopped)
00101 {
00102 m_stop = true;
00103 m_engine_wait.wakeAll();
00104 usleep(1000);
00105 }
00106 pthread_join(m_engineThread, NULL);
00107 }
00108 }
00109
00110
00111
00112 void MHIContext::Restart(uint chanid, uint cardid, bool isLive)
00113 {
00114 m_currentChannel = (chanid) ? (int)chanid : -1;
00115 m_currentCard = cardid;
00116
00117 if (m_currentChannel == m_tuningTo && m_currentChannel != -1)
00118 {
00119
00120
00121
00122
00123 if (!m_dsmcc)
00124 m_dsmcc = new Dsmcc();
00125 {
00126 QMutexLocker locker(&m_dsmccLock);
00127 m_dsmcc->Reset();
00128 m_dsmccQueue.clear();
00129 }
00130 }
00131 else
00132 {
00133 StopEngine();
00134
00135 if (!m_dsmcc)
00136 m_dsmcc = new Dsmcc();
00137
00138 {
00139 QMutexLocker locker(&m_dsmccLock);
00140 m_dsmcc->Reset();
00141 m_dsmccQueue.clear();
00142 }
00143
00144 {
00145 QMutexLocker locker(&m_keyLock);
00146 m_keyQueue.clear();
00147 }
00148
00149 if (!m_engine)
00150 m_engine = MHCreateEngine(this);
00151
00152 m_engine->SetBooting();
00153 m_display.clear();
00154 m_updated = true;
00155 m_stop = false;
00156 m_isLive = isLive;
00157
00158
00159 m_stopped = pthread_create(&m_engineThread, NULL,
00160 StartMHEGEngine, this) != 0;
00161 m_audioTag = -1;
00162 m_videoTag = -1;
00163 m_tuningTo = -1;
00164 }
00165 }
00166
00167
00168 void *MHIContext::StartMHEGEngine(void *param)
00169 {
00170
00171 MHIContext *context = (MHIContext*) param;
00172 context->RunMHEGEngine();
00173 context->m_stopped = true;
00174 return NULL;
00175 }
00176
00177 void MHIContext::RunMHEGEngine(void)
00178 {
00179 while (!m_stop)
00180 {
00181 int toWait;
00182
00183 int key = 0;
00184 do
00185 {
00186 (void)NetworkBootRequested();
00187 ProcessDSMCCQueue();
00188 {
00189 QMutexLocker locker(&m_keyLock);
00190 if (m_keyQueue.empty())
00191 key = 0;
00192 else
00193 {
00194 key = m_keyQueue.last();
00195 m_keyQueue.pop_back();
00196 }
00197 }
00198
00199 if (key != 0)
00200 m_engine->GenerateUserAction(key);
00201
00202
00203 toWait = m_engine->RunAll();
00204 if (toWait < 0)
00205 return;
00206 } while (key != 0);
00207
00208 if (toWait > 1000 || toWait == 0)
00209 toWait = 1000;
00210
00211 m_engine_wait.wait(toWait);
00212 }
00213 }
00214
00215
00216 void MHIContext::ProcessDSMCCQueue(void)
00217 {
00218 DSMCCPacket *packet = NULL;
00219 do
00220 {
00221 {
00222 QMutexLocker locker(&m_dsmccLock);
00223 packet = m_dsmccQueue.dequeue();
00224 }
00225
00226 if (packet)
00227 {
00228 m_dsmcc->ProcessSection(
00229 packet->m_data, packet->m_length,
00230 packet->m_componentTag, packet->m_carouselId,
00231 packet->m_dataBroadcastId);
00232
00233 delete packet;
00234 }
00235 } while (packet);
00236 }
00237
00238 void MHIContext::QueueDSMCCPacket(
00239 unsigned char *data, int length, int componentTag,
00240 unsigned carouselId, int dataBroadcastId)
00241 {
00242 unsigned char *dataCopy =
00243 (unsigned char*) malloc(length * sizeof(unsigned char));
00244
00245 if (dataCopy == NULL)
00246 return;
00247
00248 memcpy(dataCopy, data, length*sizeof(unsigned char));
00249 QMutexLocker locker(&m_dsmccLock);
00250 m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy, length,
00251 componentTag, carouselId,
00252 dataBroadcastId));
00253 m_engine_wait.wakeAll();
00254 }
00255
00256
00257 void MHIContext::SetNetBootInfo(const unsigned char *data, uint length)
00258 {
00259 if (length < 2) return;
00260 QMutexLocker locker(&m_dsmccLock);
00261
00262 m_nbiData.duplicate(data, length);
00263
00264
00265 if (length < 2)
00266 m_lastNbiVersion = NBI_VERSION_ABSENT;
00267 else if (m_lastNbiVersion == NBI_VERSION_UNSET)
00268 m_lastNbiVersion = data[0];
00269 else
00270 m_engine_wait.wakeAll();
00271 }
00272
00273 void MHIContext::NetworkBootRequested(void)
00274 {
00275 QMutexLocker locker(&m_dsmccLock);
00276 if (m_nbiData.size() >= 2 && m_nbiData[0] != m_lastNbiVersion)
00277 {
00278 m_lastNbiVersion = m_nbiData[0];
00279 if (m_nbiData[1] == 1)
00280 {
00281 m_dsmcc->Reset();
00282 m_engine->SetBooting();
00283 m_display.clear();
00284 m_updated = true;
00285 }
00286
00287 }
00288 }
00289
00290
00291 bool MHIContext::CheckCarouselObject(QString objectPath)
00292 {
00293 QStringList path = QStringList::split(QChar('/'), objectPath);
00294 QByteArray result;
00295 int res = m_dsmcc->GetDSMCCObject(path, result);
00296 return res == 0;
00297 }
00298
00299
00300 bool MHIContext::GetCarouselData(QString objectPath, QByteArray &result)
00301 {
00302
00303
00304 QStringList path = QStringList::split(QChar('/'), objectPath);
00305
00306
00307
00308 while (!m_stop)
00309 {
00310 int res = m_dsmcc->GetDSMCCObject(path, result);
00311 if (res == 0)
00312 return true;
00313 else if (res < 0)
00314 return false;
00315
00316
00317
00318
00319 ProcessDSMCCQueue();
00320 m_engine_wait.wait(1000);
00321 }
00322 return false;
00323 }
00324
00325
00326
00327
00328 bool MHIContext::OfferKey(QString key)
00329 {
00330 int action = 0;
00331 QMutexLocker locker(&m_keyLock);
00332
00333
00334
00335
00336
00337 if (key == "UP")
00338 {
00339 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00340 m_keyProfile == 14 || m_keyProfile == 15)
00341 action = 1;
00342 }
00343 else if (key == "DOWN")
00344 {
00345 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00346 m_keyProfile == 14 || m_keyProfile == 15)
00347 action = 2;
00348 }
00349 else if (key == "LEFT")
00350 {
00351 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00352 m_keyProfile == 14 || m_keyProfile == 15)
00353 action = 3;
00354 }
00355 else if (key == "RIGHT")
00356 {
00357 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00358 m_keyProfile == 14 || m_keyProfile == 15)
00359 action = 4;
00360 }
00361 else if (key == "0" || key == "1" || key == "2" ||
00362 key == "3" || key == "4" || key == "5" ||
00363 key == "6" || key == "7" || key == "8" ||
00364 key == "9")
00365 {
00366 if (m_keyProfile == 4 || m_keyProfile == 14)
00367 action = key.toInt() + 5;
00368 }
00369 else if (key == "SELECT")
00370 {
00371 if (m_keyProfile == 4 || m_keyProfile == 5 ||
00372 m_keyProfile == 14 || m_keyProfile == 15)
00373 action = 15;
00374 }
00375 else if (key == "TEXTEXIT")
00376 action = 16;
00377 else if (key == "MENURED")
00378 action = 100;
00379 else if (key == "MENUGREEN")
00380 action = 101;
00381 else if (key == "MENUYELLOW")
00382 action = 102;
00383 else if (key == "MENUBLUE")
00384 action = 103;
00385 else if (key == "MENUTEXT")
00386 action = m_keyProfile > 12 ? 105 : 104;
00387 else if (key == "MENUEPG")
00388 action = m_keyProfile > 12 ? 300 : 0;
00389
00390 if (action != 0)
00391 {
00392 m_keyQueue.push_front(action);
00393 VERBOSE(VB_IMPORTANT, "Adding MHEG key "<<key<<":"<<action
00394 <<":"<<m_keyQueue.size());
00395 m_engine_wait.wakeAll();
00396 return true;
00397 }
00398
00399 return false;
00400 }
00401
00402 void MHIContext::Reinit(const QRect &display)
00403 {
00404 m_displayWidth = display.width();
00405 m_displayHeight = display.height();
00406 m_videoRect = display;
00407 }
00408
00409 void MHIContext::SetInputRegister(int num)
00410 {
00411 QMutexLocker locker(&m_keyLock);
00412 m_keyQueue.clear();
00413 m_keyProfile = num;
00414 }
00415
00416
00417
00418 void MHIContext::UpdateOSD(OSDSet *osdSet)
00419 {
00420 QMutexLocker locker(&m_display_lock);
00421 m_updated = false;
00422 osdSet->Clear();
00423
00424 for (MHIImageData *data = m_display.first(); data;
00425 data = m_display.next())
00426 {
00427 OSDTypeImage* image = new OSDTypeImage();
00428 image->SetPosition(QPoint(data->m_x, data->m_y), 1.0, 1.0);
00429 image->Load(data->m_image);
00430 osdSet->AddType(image);
00431 }
00432 }
00433
00434 void MHIContext::GetInitialStreams(int &audioTag, int &videoTag)
00435 {
00436 audioTag = m_audioTag;
00437 videoTag = m_videoTag;
00438 }
00439
00440
00441
00442
00443
00444 void MHIContext::RequireRedraw(const QRegion &)
00445 {
00446 m_display_lock.lock();
00447 m_display.clear();
00448 m_display_lock.unlock();
00449
00450 m_engine->DrawDisplay(QRegion(0, 0, StdDisplayWidth, StdDisplayHeight));
00451 m_updated = true;
00452 }
00453
00454 void MHIContext::AddToDisplay(const QImage &image, int x, int y)
00455 {
00456 MHIImageData *data = new MHIImageData;
00457
00458
00459
00460
00461 QImage img = image;
00462 int xboundary = x & 1;
00463 int yboundary = y & 1;
00464
00465 if (xboundary || yboundary)
00466 {
00467 int width = img.width(), height = img.height();
00468 if (xboundary)
00469 {
00470 width++;
00471 x--;
00472 }
00473 if (yboundary)
00474 {
00475 height++;
00476 y--;
00477 }
00478 img = QImage(width, height, 32);
00479 img.setAlphaBuffer(true);
00480 QRgb qTransparent = qRgba(0,0,0,0);
00481 if (xboundary)
00482 {
00483 for (int i = 0; i < height; i++)
00484 img.setPixel(0, i, qTransparent);
00485 }
00486
00487 if (yboundary)
00488 {
00489 for (int j = 0; j < width; j++)
00490 img.setPixel(j, 0, qTransparent);
00491 }
00492
00493 for (int i = 0; i < height-yboundary; i++)
00494 {
00495 for (int j = 0; j < width-xboundary; j++)
00496 {
00497 img.setPixel(j+xboundary, i+yboundary, image.pixel(j,i));
00498 }
00499 }
00500 }
00501 data->m_image = img;
00502 data->m_x = x;
00503 data->m_y = y;
00504 QMutexLocker locker(&m_display_lock);
00505 m_display.append(data);
00506 }
00507
00508
00509
00510
00511
00512
00513
00514
00515 void MHIContext::DrawVideo(const QRect &videoRect, const QRect &dispRect)
00516 {
00517
00518 if (m_parent->GetNVP())
00519 {
00520 QRect vidRect(videoRect.x() * m_displayWidth/StdDisplayWidth,
00521 videoRect.y() * m_displayHeight/StdDisplayHeight,
00522 videoRect.width() * m_displayWidth/StdDisplayWidth,
00523 videoRect.height() * m_displayHeight/StdDisplayHeight);
00524 if (m_videoRect != vidRect)
00525 {
00526 m_parent->GetNVP()->SetVideoResize(vidRect);
00527 m_videoRect = vidRect;
00528 }
00529 }
00530
00531 QMutexLocker locker(&m_display_lock);
00532 QRect displayRect(dispRect.x() * m_displayWidth/StdDisplayWidth,
00533 dispRect.y() * m_displayHeight/StdDisplayHeight,
00534 dispRect.width() * m_displayWidth/StdDisplayWidth,
00535 dispRect.height() * m_displayHeight/StdDisplayHeight);
00536
00537 for (uint i = 0; i < m_display.count(); i++)
00538 {
00539 MHIImageData *data = m_display.at(i);
00540 QRect imageRect(data->m_x, data->m_y,
00541 data->m_image.width(), data->m_image.height());
00542 if (displayRect.intersects(imageRect))
00543 {
00544
00545 (void)m_display.take(i--);
00546 QMemArray<QRect> rects = (QRegion(imageRect)
00547 - QRegion(displayRect)).rects();
00548 for (uint j = 0; j < rects.size(); j++)
00549 {
00550 QRect &rect = rects[j];
00551 QImage image =
00552 data->m_image.copy(rect.x()-data->m_x, rect.y()-data->m_y,
00553 rect.width(), rect.height());
00554 MHIImageData *newData = new MHIImageData;
00555 newData->m_image = image;
00556 newData->m_x = rect.x();
00557 newData->m_y = rect.y();
00558 m_display.insert(++i, newData);
00559 }
00560 delete(data);
00561 }
00562 }
00563 }
00564
00565
00566
00567
00568
00569
00570
00571
00572 int MHIContext::GetChannelIndex(const QString &str)
00573 {
00574 if (str.startsWith("dvb://"))
00575 {
00576 QStringList list = QStringList::split('.', str.mid(6), true);
00577 MSqlQuery query(MSqlQuery::InitCon());
00578 if (list.size() != 3) return -1;
00579
00580
00581 bool ok;
00582 int netID = list[0].toInt(&ok, 16);
00583 if (!ok)
00584 return -1;
00585 int serviceID = list[2].toInt(&ok, 16);
00586 if (!ok)
00587 return -1;
00588
00589 if (list[1].isEmpty())
00590 {
00591 query.prepare(
00592 "SELECT chanid "
00593 "FROM channel, dtv_multiplex, cardinput, capturecard "
00594 "WHERE networkid = :NETID AND"
00595 " channel.mplexid = dtv_multiplex.mplexid AND "
00596 " serviceid = :SERVICEID AND "
00597 " channel.sourceid = cardinput.sourceid AND "
00598 " cardinput.cardid = capturecard.cardid AND "
00599 " cardinput.cardid = :CARDID");
00600 }
00601 else
00602 {
00603 int transportID = list[1].toInt(&ok, 16);
00604 if (!ok)
00605 return -1;
00606 query.prepare(
00607 "SELECT chanid "
00608 "FROM channel, dtv_multiplex, cardinput, capturecard "
00609 "WHERE networkid = :NETID AND"
00610 " channel.mplexid = dtv_multiplex.mplexid AND "
00611 " serviceid = :SERVICEID AND "
00612 " transportid = :TRANSID AND "
00613 " channel.sourceid = cardinput.sourceid AND "
00614 " cardinput.cardid = capturecard.cardid AND "
00615 " cardinput.cardid = :CARDID");
00616 query.bindValue(":TRANSID", transportID);
00617 }
00618 query.bindValue(":NETID", netID);
00619 query.bindValue(":SERVICEID", serviceID);
00620 query.bindValue(":CARDID", m_currentCard);
00621 if (query.exec() && query.isActive() && query.next())
00622 {
00623 int nResult = query.value(0).toInt();
00624 return nResult;
00625 }
00626 }
00627 else if (str.startsWith("rec://svc/lcn/"))
00628 {
00629
00630 bool ok;
00631 int channelNo = str.mid(14).toInt(&ok);
00632 if (!ok) return -1;
00633 MSqlQuery query(MSqlQuery::InitCon());
00634 query.prepare("SELECT chanid "
00635 "FROM channel, cardinput, capturecard "
00636 "WHERE channum = :CHAN AND "
00637 " channel.sourceid = cardinput.sourceid AND "
00638 " cardinput.cardid = capturecard.cardid AND "
00639 " cardinput.cardid = :CARDID");
00640 query.bindValue(":CHAN", channelNo);
00641 query.bindValue(":CARDID", m_currentCard);
00642 if (query.exec() && query.isActive() && query.next())
00643 return query.value(0).toInt();
00644 }
00645 else if (str == "rec://svc/cur" || str == "rec://svc/def")
00646 return m_currentChannel;
00647 else if (str.startsWith("rec://"))
00648 {
00649 }
00650 return -1;
00651 }
00652
00653
00654 bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId,
00655 int &transportId, int &serviceId)
00656 {
00657 MSqlQuery query(MSqlQuery::InitCon());
00658 query.prepare("SELECT networkid, transportid, serviceid "
00659 "FROM channel, dtv_multiplex "
00660 "WHERE chanid = :CHANID AND "
00661 " channel.mplexid = dtv_multiplex.mplexid");
00662 query.bindValue(":CHANID", channelId);
00663 if (query.exec() && query.isActive() && query.next())
00664 {
00665 netId = query.value(0).toInt();
00666 origNetId = netId;
00667 transportId = query.value(1).toInt();
00668 serviceId = query.value(2).toInt();
00669 return true;
00670 }
00671 else return false;
00672 }
00673
00674 bool MHIContext::TuneTo(int channel)
00675 {
00676 if (!m_isLive)
00677 return false;
00678
00679
00680 MythEvent me(QString("NETWORK_CONTROL CHANID %1").arg(channel));
00681 gContext->dispatch(me);
00682
00683 QMutexLocker locker(&m_dsmccLock);
00684 m_lastNbiVersion = NBI_VERSION_UNSET;
00685 m_nbiData.resize(0);
00686 return true;
00687 }
00688
00689
00690 bool MHIContext::BeginAudio(const QString &stream, int tag)
00691 {
00692 int chan = GetChannelIndex(stream);
00693
00694 if (chan != m_currentChannel)
00695 {
00696
00697
00698
00699 m_tuningTo = chan;
00700 m_audioTag = tag;
00701 return TuneTo(chan);
00702 }
00703
00704 if (tag < 0)
00705 return true;
00706 else if (m_parent->GetNVP())
00707 return m_parent->GetNVP()->SetAudioByComponentTag(tag);
00708 else
00709 return false;
00710 }
00711
00712
00713 void MHIContext::StopAudio(void)
00714 {
00715
00716 }
00717
00718
00719 bool MHIContext::BeginVideo(const QString &stream, int tag)
00720 {
00721 int chan = GetChannelIndex(stream);
00722 if (chan != m_currentChannel)
00723 {
00724
00725 m_tuningTo = chan;
00726 m_videoTag = tag;
00727 return TuneTo(chan);
00728 }
00729 if (tag < 0)
00730 return true;
00731 else if (m_parent->GetNVP())
00732 return m_parent->GetNVP()->SetVideoByComponentTag(tag);
00733
00734 return false;
00735 }
00736
00737
00738 void MHIContext::StopVideo(void)
00739 {
00740
00741 }
00742
00743
00744 MHDLADisplay *MHIContext::CreateDynamicLineArt(
00745 bool isBoxed, MHRgba lineColour, MHRgba fillColour)
00746 {
00747 return new MHIDLA(this, isBoxed, lineColour, fillColour);
00748 }
00749
00750
00751 MHTextDisplay *MHIContext::CreateText()
00752 {
00753 return new MHIText(this);
00754 }
00755
00756
00757 MHBitmapDisplay *MHIContext::CreateBitmap(bool tiled)
00758 {
00759 return new MHIBitmap(this, tiled);
00760 }
00761
00762
00763 void MHIContext::DrawRect(int xPos, int yPos, int width, int height,
00764 MHRgba colour)
00765 {
00766 if (colour.alpha() == 0 || height == 0 || width == 0)
00767 return;
00768
00769 QRgb qColour = qRgba(colour.red(), colour.green(),
00770 colour.blue(), colour.alpha());
00771
00772 int scaledWidth = width * GetWidth() / MHIContext::StdDisplayWidth;
00773 int scaledHeight = height * GetHeight() / MHIContext::StdDisplayHeight;
00774 QImage qImage(scaledWidth, scaledHeight, 32);
00775 qImage.setAlphaBuffer(true);
00776
00777 for (int i = 0; i < scaledHeight; i++)
00778 {
00779 for (int j = 0; j < scaledWidth; j++)
00780 {
00781 qImage.setPixel(j, i, qColour);
00782 }
00783 }
00784
00785 AddToDisplay(qImage,
00786 xPos * GetWidth() / MHIContext::StdDisplayWidth,
00787 yPos * GetHeight() / MHIContext::StdDisplayHeight);
00788 }
00789
00790
00791
00792
00793
00794
00795 void MHIContext::DrawImage(int x, int y, const QRect &clipRect,
00796 const QImage &qImage)
00797 {
00798 if (qImage.isNull())
00799 return;
00800
00801 QRect imageRect(x, y, qImage.width(), qImage.height());
00802 QRect displayRect = QRect(clipRect.x(), clipRect.y(),
00803 clipRect.width(), clipRect.height()) & imageRect;
00804
00805 if (displayRect == imageRect)
00806 {
00807
00808
00809 QImage scaled =
00810 qImage.smoothScale(
00811 displayRect.width() * GetWidth() / MHIContext::StdDisplayWidth,
00812 displayRect.height() *
00813 GetHeight() / MHIContext::StdDisplayHeight);
00814 AddToDisplay(scaled.convertDepth(32),
00815 x * GetWidth() / MHIContext::StdDisplayWidth,
00816 y * GetHeight() / MHIContext::StdDisplayHeight);
00817 }
00818 else if (!displayRect.isEmpty())
00819 {
00820 QImage clipped = qImage.convertDepth(32)
00821 .copy(displayRect.x() - x, displayRect.y() - y,
00822 displayRect.width(), displayRect.height());
00823 QImage scaled =
00824 clipped.smoothScale(
00825 displayRect.width() * GetWidth() / MHIContext::StdDisplayWidth,
00826 displayRect.height() *
00827 GetHeight() / MHIContext::StdDisplayHeight);
00828 AddToDisplay(scaled,
00829 displayRect.x() *
00830 GetWidth() / MHIContext::StdDisplayWidth,
00831 displayRect.y() *
00832 GetHeight() / MHIContext::StdDisplayHeight);
00833 }
00834
00835 }
00836
00837
00838
00839 void MHIContext::DrawBackground(const QRegion ®)
00840 {
00841 if (reg.isNull() || reg.isEmpty())
00842 return;
00843
00844 QRect bounds = reg.boundingRect();
00845 DrawRect(bounds.x(), bounds.y(), bounds.width(), bounds.height(),
00846 MHRgba(0, 0, 0, 255));
00847 }
00848
00849 MHIText::MHIText(MHIContext *parent): m_parent(parent)
00850 {
00851 m_fontsize = 12;
00852 m_fontItalic = false;
00853 m_fontBold = false;
00854 }
00855
00856 void MHIText::Draw(int x, int y)
00857 {
00858 m_parent->DrawImage(x, y, QRect(x, y, m_width, m_height), m_image);
00859 }
00860
00861 void MHIText::SetSize(int width, int height)
00862 {
00863 m_width = width;
00864 m_height = height;
00865 }
00866
00867 void MHIText::SetFont(int size, bool isBold, bool isItalic)
00868 {
00869 m_fontsize = size;
00870 m_fontItalic = isItalic;
00871 m_fontBold = isBold;
00872
00873
00874 }
00875
00876
00877
00878
00879
00880
00881
00882
00883 QRect MHIText::GetBounds(const QString &str, int &strLen, int maxSize)
00884 {
00885 if (!m_parent->IsFaceLoaded())
00886 return QRect(0,0,0,0);
00887
00888 FT_Face face = m_parent->GetFontFace();
00889 FT_Error error = FT_Set_Char_Size(face, 0, m_fontsize*64,
00890 FONT_WIDTHRES, FONT_HEIGHTRES);
00891 if (error)
00892 return QRect(0,0,0,0);
00893
00894 FT_GlyphSlot slot = face->glyph;
00895
00896 int maxAscent = 0, maxDescent = 0, width = 0;
00897 FT_Bool useKerning = FT_HAS_KERNING(face);
00898 FT_UInt previous = 0;
00899
00900 for (int n = 0; n < strLen; n++)
00901 {
00902 QChar ch = str[n];
00903 FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
00904 int kerning = 0;
00905
00906 if (useKerning && previous != 0 && glyphIndex != 0)
00907 {
00908 FT_Vector delta;
00909 FT_Get_Kerning(face, previous, glyphIndex,
00910 FT_KERNING_DEFAULT, &delta);
00911 kerning = delta.x;
00912 }
00913
00914 error = FT_Load_Glyph(face, glyphIndex, 0);
00915
00916 if (error)
00917 continue;
00918
00919 if (maxSize >= 0)
00920 {
00921 if ((width + slot->advance.x + kerning + (1<<6)-1) >> 6 > maxSize)
00922 {
00923
00924 strLen = n;
00925 break;
00926 }
00927 }
00928
00929 int descent = slot->metrics.height - slot->metrics.horiBearingY;
00930
00931 if (slot->metrics.horiBearingY > maxAscent)
00932 maxAscent = slot->metrics.horiBearingY;
00933
00934 if (descent > maxDescent)
00935 maxDescent = descent;
00936
00937 width += slot->advance.x + kerning;
00938 previous = glyphIndex;
00939 }
00940
00941 maxAscent = (maxAscent + (1<<6)-1) >> 6;
00942 maxDescent = (maxDescent + (1<<6)-1) >> 6;
00943
00944 return QRect(0, -maxAscent,
00945 (width+(1<<6)-1) >> 6, maxAscent + maxDescent);
00946 }
00947
00948
00949
00950
00951
00952 void MHIText::Clear(void)
00953 {
00954 m_image = QImage(m_width, m_height, 32);
00955
00956 m_image.setAlphaBuffer(true);
00957
00958 for (int i = 0; i < m_height; i++)
00959 {
00960 for (int j = 0; j < m_width; j++)
00961 {
00962 m_image.setPixel(j, i, qRgba(0, 0, 0, 0));
00963 }
00964 }
00965 }
00966
00967
00968
00969
00970 void MHIText::AddText(int x, int y, const QString &str, MHRgba colour)
00971 {
00972 if (!m_parent->IsFaceLoaded()) return;
00973 FT_Face face = m_parent->GetFontFace();
00974 FT_GlyphSlot slot = face->glyph;
00975 FT_Error error = FT_Set_Char_Size(face, 0, m_fontsize*64,
00976 FONT_WIDTHRES, FONT_HEIGHTRES);
00977
00978
00979
00980 int posX = x << 6;
00981 int pixelY = y;
00982 FT_Bool useKerning = FT_HAS_KERNING(face);
00983 FT_UInt previous = 0;
00984
00985 int len = str.length();
00986 for (int n = 0; n < len; n++)
00987 {
00988
00989 QChar ch = str[n];
00990 FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
00991 if (useKerning && previous != 0 && glyphIndex != 0)
00992 {
00993 FT_Vector delta;
00994 FT_Get_Kerning(face, previous, glyphIndex,
00995 FT_KERNING_DEFAULT, &delta);
00996 posX += delta.x;
00997 }
00998 error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
00999
01000 if (error)
01001 continue;
01002
01003 if (slot->format != FT_GLYPH_FORMAT_BITMAP)
01004 continue;
01005
01006 if ((enum FT_Pixel_Mode_)slot->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
01007 continue;
01008
01009 unsigned char *source = slot->bitmap.buffer;
01010
01011 int baseX = ((posX + (1 << 5)) >> 6) + slot->bitmap_left;
01012 int baseY = pixelY - slot->bitmap_top;
01013
01014 for (int i = 0; i < slot->bitmap.rows; i++)
01015 {
01016 for (int j = 0; j < slot->bitmap.width; j++)
01017 {
01018 int greyLevel = source[j];
01019
01020
01021 int red = colour.red();
01022 int green = colour.green();
01023 int blue = colour.blue();
01024 int alpha = colour.alpha() *
01025 greyLevel / slot->bitmap.num_grays;
01026 int xBit = j + baseX;
01027 int yBit = i + baseY;
01028
01029
01030
01031
01032 if (xBit >= 0 && xBit < m_width &&
01033 yBit >= 0 && yBit < m_height)
01034 {
01035 m_image.setPixel(xBit, yBit,
01036 qRgba(red, green, blue, alpha));
01037 }
01038 }
01039 source += slot->bitmap.pitch;
01040 }
01041 posX += slot->advance.x;
01042 previous = glyphIndex;
01043 }
01044 }
01045
01046
01047 void MHIDLA::DrawRect(int x, int y, int width, int height, MHRgba colour)
01048 {
01049 QRgb qColour = qRgba(colour.red(), colour.green(),
01050 colour.blue(), colour.alpha());
01051
01052
01053 if (x < 0)
01054 {
01055 width += x;
01056 x = 0;
01057 }
01058
01059 if (y < 0)
01060 {
01061 height += y;
01062 y = 0;
01063 }
01064
01065 if (width <= 0 || height <= 0)
01066 return;
01067
01068 int imageWidth = m_image.width(), imageHeight = m_image.height();
01069 if (x+width > imageWidth)
01070 width = imageWidth - x;
01071
01072 if (y+height > imageHeight)
01073 height = imageHeight - y;
01074
01075 if (width <= 0 || height <= 0)
01076 return;
01077
01078 for (int i = 0; i < height; i++)
01079 {
01080 for (int j = 0; j < width; j++)
01081 {
01082 m_image.setPixel(x+j, y+i, qColour);
01083 }
01084 }
01085 }
01086
01087
01088 void MHIDLA::Clear()
01089 {
01090 if (m_width == 0 || m_height == 0)
01091 {
01092 m_image = QImage();
01093 return;
01094 }
01095 m_image = QImage(m_width, m_height, 32);
01096
01097 DrawRect(0, 0, m_width, m_height, MHRgba(0, 0, 0, 0));
01098 }
01099
01100 void MHIDLA::Draw(int x, int y)
01101 {
01102 QRect bounds(x, y, m_width, m_height);
01103 if (m_boxed && m_lineWidth != 0)
01104 {
01105
01106
01107 m_parent->DrawRect(x, y, m_width,
01108 m_lineWidth, m_boxLineColour);
01109
01110 m_parent->DrawRect(x, y + m_height - m_lineWidth,
01111 m_width, m_lineWidth, m_boxLineColour);
01112
01113 m_parent->DrawRect(x, y + m_lineWidth,
01114 m_lineWidth, m_height - m_lineWidth * 2,
01115 m_boxLineColour);
01116
01117 m_parent->DrawRect(x + m_width - m_lineWidth, y + m_lineWidth,
01118 m_lineWidth, m_height - m_lineWidth * 2,
01119 m_boxLineColour);
01120
01121
01122 bounds = QRect(bounds.x() + m_lineWidth,
01123 bounds.y() + m_lineWidth,
01124 bounds.width() - 2*m_lineWidth,
01125 bounds.height() - 2*m_lineWidth);
01126 }
01127
01128
01129 m_parent->DrawRect(x + m_lineWidth,
01130 y + m_lineWidth,
01131 m_width - m_lineWidth * 2,
01132 m_height - m_lineWidth * 2,
01133 m_boxFillColour);
01134
01135
01136 m_parent->DrawImage(x, y, bounds, m_image);
01137 }
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148 void MHIDLA::DrawLine(int x1, int y1, int x2, int y2)
01149 {
01150
01151
01152 if (abs(y2-y1) > abs(x2-x1))
01153 {
01154 if (y2 > y1)
01155 DrawLineSub(y1, x1, y2, x2, true);
01156 else
01157 DrawLineSub(y2, x2, y1, x1, true);
01158 }
01159 else
01160 {
01161 if (x2 > x1)
01162 DrawLineSub(x1, y1, x2, y2, false);
01163 else
01164 DrawLineSub(x2, y2, x1, y1, false);
01165 }
01166 }
01167
01168
01169
01170 void MHIDLA::DrawLineSub(int x1, int y1, int x2, int y2, bool swapped)
01171 {
01172 QRgb colour = qRgba(m_lineColour.red(), m_lineColour.green(),
01173 m_lineColour.blue(), m_lineColour.alpha());
01174 int dx = x2-x1, dy = abs(y2-y1);
01175 int yStep = y2 >= y1 ? 1 : -1;
01176
01177
01178 int error2 = dx/2;
01179 for (int k = 0; k < m_lineWidth/2; k++)
01180 {
01181 y1--;
01182 y2--;
01183 error2 += dy;
01184 if (error2*2 > dx)
01185 {
01186 error2 -= dx;
01187 x1 += yStep;
01188 x2 += yStep;
01189 }
01190 }
01191
01192 int y = y1;
01193 int error = dx/2;
01194 for (int x = x1; x <= x2; x++)
01195 {
01196 error2 = dx/2;
01197 int j = 0;
01198
01199
01200 for (int i = 0; i < m_lineWidth; i++)
01201 {
01202 if (swapped)
01203 {
01204 if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
01205 m_image.setPixel(y+i, x+j, colour);
01206 }
01207 else
01208 {
01209 if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
01210 m_image.setPixel(x+j, y+i, colour);
01211 }
01212 error2 += dy;
01213 if (error2*2 > dx)
01214 {
01215 error2 -= dx;
01216 j -= yStep;
01217 if (i < m_lineWidth-1)
01218 {
01219
01220 if (swapped)
01221 {
01222 if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
01223 m_image.setPixel(y+i, x+j, colour);
01224 }
01225 else
01226 {
01227 if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
01228 m_image.setPixel(x+j, y+i, colour);
01229 }
01230 }
01231 }
01232 }
01233 error += dy;
01234 if (error*2 > dx)
01235 {
01236 error -= dx;
01237 y += yStep;
01238 }
01239 }
01240 }
01241
01242
01243 void MHIDLA::DrawBorderedRectangle(int x, int y, int width, int height)
01244 {
01245 if (m_lineWidth != 0)
01246 {
01247
01248 DrawRect(x, y, width, m_lineWidth,
01249 m_lineColour);
01250
01251 DrawRect(x, y + height - m_lineWidth,
01252 width, m_lineWidth,
01253 m_lineColour);
01254
01255 DrawRect(x, y + m_lineWidth,
01256 m_lineWidth, height - m_lineWidth * 2,
01257 m_lineColour);
01258
01259 DrawRect(x + width - m_lineWidth, y + m_lineWidth,
01260 m_lineWidth, height - m_lineWidth * 2,
01261 m_lineColour);
01262
01263
01264 DrawRect(x + m_lineWidth, y + m_lineWidth,
01265 width - m_lineWidth * 2, height - m_lineWidth * 2,
01266 m_fillColour);
01267 }
01268 else
01269 {
01270 DrawRect(x, y, width, height, m_fillColour);
01271 }
01272 }
01273
01274
01275 void MHIDLA::DrawOval(int x, int y, int width, int height)
01276 {
01277
01278 QPointArray ellipse;
01279 ellipse.makeEllipse(x, y, width, height);
01280 DrawPoly(true, ellipse);
01281 }
01282
01283
01284 void MHIDLA::DrawArcSector(int x, int y, int width, int height,
01285 int start, int arc, bool isSector)
01286 {
01287 QPointArray points;
01288
01289
01290
01291 points.makeArc(x, y, width, height, start/4, arc/4);
01292 if (isSector)
01293 {
01294
01295 if (arc != 360*64)
01296 points.putPoints(points.size(), 1, x+width/2, y+height/2);
01297 DrawPoly(true, points);
01298 }
01299 else
01300 DrawPoly(false, points);
01301 }
01302
01303
01304
01305
01306
01307 typedef struct { int yBottom, yTop, xBottom; float slope; } lineSeg;
01308
01309 void MHIDLA::DrawPoly(bool isFilled, const QPointArray &points)
01310 {
01311 int nPoints = points.size();
01312 if (nPoints < 2)
01313 return;
01314
01315 if (isFilled)
01316 {
01317 QMemArray <lineSeg> lineArray(nPoints);
01318 int nLines = 0;
01319
01320
01321
01322 int lastX = points[nPoints-1].x();
01323 int lastY = points[nPoints-1].y();
01324 int yMin = lastY, yMax = lastY;
01325 for (int k = 0; k < nPoints; k++)
01326 {
01327 int thisX = points[k].x();
01328 int thisY = points[k].y();
01329 if (lastY != thisY)
01330 {
01331 if (lastY > thisY)
01332 {
01333 lineArray[nLines].yBottom = thisY;
01334 lineArray[nLines].yTop = lastY;
01335 lineArray[nLines].xBottom = thisX;
01336 }
01337 else
01338 {
01339 lineArray[nLines].yBottom = lastY;
01340 lineArray[nLines].yTop = thisY;
01341 lineArray[nLines].xBottom = lastX;
01342 }
01343 lineArray[nLines++].slope =
01344 (float)(thisX-lastX) / (float)(thisY-lastY);
01345 }
01346 if (thisY < yMin)
01347 yMin = thisY;
01348 if (thisY > yMax)
01349 yMax = thisY;
01350 lastX = thisX;
01351 lastY = thisY;
01352 }
01353
01354
01355
01356
01357 QRgb fillColour = qRgba(m_fillColour.red(), m_fillColour.green(),
01358 m_fillColour.blue(), m_fillColour.alpha());
01359 for (int y = yMin; y < yMax; y++)
01360 {
01361 int crossings = 0, xMin = 0, xMax = 0;
01362 for (int l = 0; l < nLines; l++)
01363 {
01364 if (y >= lineArray[l].yBottom && y < lineArray[l].yTop)
01365 {
01366 int x = (int)round((float)(y - lineArray[l].yBottom) *
01367 lineArray[l].slope) + lineArray[l].xBottom;
01368 if (crossings == 0 || x < xMin)
01369 xMin = x;
01370 if (crossings == 0 || x > xMax)
01371 xMax = x;
01372 crossings++;
01373 }
01374 }
01375 if (crossings == 2)
01376 {
01377 for (int x = xMin; x <= xMax; x++)
01378 m_image.setPixel(x, y, fillColour);
01379 }
01380 }
01381
01382
01383 QPoint last = points[nPoints-1];
01384 for (int i = 0; i < nPoints; i++)
01385 {
01386 DrawLine(points[i].x(), points[i].y(), last.x(), last.y());
01387 last = points[i];
01388 }
01389 }
01390 else
01391 {
01392 for (int i = 1; i < nPoints; i++)
01393 {
01394 DrawLine(points[i].x(), points[i].y(), points[i-1].x(), points[i-1].y());
01395 }
01396 }
01397 }
01398
01399
01400 void MHIBitmap::Draw(int x, int y, QRect rect, bool tiled)
01401 {
01402 if (tiled)
01403 {
01404 if (m_image.width() == 0 || m_image.height() == 0)
01405 return;
01406
01407
01408 QImage tiledImage = QImage(rect.width(), rect.height(), 32);
01409
01410 for (int i = 0; i < rect.width(); i += m_image.width())
01411 {
01412 for (int j = 0; j < rect.height(); j += m_image.height())
01413 {
01414 bitBlt(&tiledImage, i, j, &m_image, 0, 0, -1, -1, 0);
01415 }
01416 }
01417 m_parent->DrawImage(rect.x(), rect.y(), rect, tiledImage);
01418 }
01419 else
01420 {
01421 m_parent->DrawImage(x, y, rect, m_image);
01422 }
01423 }
01424
01425
01426 void MHIBitmap::CreateFromPNG(const unsigned char *data, int length)
01427 {
01428 m_image.reset();
01429
01430 if (!m_image.loadFromData(data, length, "PNG"))
01431 {
01432 m_image.reset();
01433 return;
01434 }
01435
01436
01437 m_opaque = ! m_image.hasAlphaBuffer();
01438 }
01439
01440
01441
01442
01443
01444
01445 void MHIBitmap::CreateFromMPEG(const unsigned char *data, int length)
01446 {
01447 AVCodecContext *c = NULL;
01448 AVFrame *picture = NULL;
01449 uint8_t *buff = NULL, *buffPtr;
01450 int gotPicture = 0, len;
01451 m_image.reset();
01452
01453
01454 AVCodec *codec = avcodec_find_decoder(CODEC_ID_MPEG2VIDEO);
01455 if (!codec)
01456 return;
01457
01458 c = avcodec_alloc_context();
01459 picture = avcodec_alloc_frame();
01460
01461 if (avcodec_open(c, codec) < 0)
01462 goto Close;
01463
01464
01465 buff = (uint8_t *)malloc(length + FF_INPUT_BUFFER_PADDING_SIZE);
01466 if (!buff)
01467 goto Close;
01468
01469 memcpy(buff, data, length);
01470 memset(buff + length, 0, FF_INPUT_BUFFER_PADDING_SIZE);
01471 buffPtr = buff;
01472
01473 while (length > 0 && ! gotPicture)
01474 {
01475 len = avcodec_decode_video(c, picture, &gotPicture, buffPtr, length);
01476 if (len < 0)
01477 goto Close;
01478 length -= len;
01479 buffPtr += len;
01480 }
01481
01482 if (!gotPicture)
01483 {
01484
01485 if (avcodec_decode_video(c, picture, &gotPicture, NULL, 0) < 0)
01486 goto Close;
01487 }
01488
01489 if (gotPicture)
01490 {
01491 int nContentWidth = c->width;
01492 int nContentHeight = c->height;
01493 m_image = QImage(nContentWidth, nContentHeight, 32);
01494 m_opaque = true;
01495
01496 AVPicture retbuf;
01497 bzero(&retbuf, sizeof(AVPicture));
01498
01499 int bufflen = nContentWidth * nContentHeight * 3;
01500 unsigned char *outputbuf = new unsigned char[bufflen];
01501
01502 avpicture_fill(&retbuf, outputbuf, PIX_FMT_RGB24,
01503 nContentWidth, nContentHeight);
01504
01505 img_convert(&retbuf, PIX_FMT_RGB24, (AVPicture*)picture, c->pix_fmt,
01506 nContentWidth, nContentHeight);
01507
01508 uint8_t * buf = outputbuf;
01509
01510
01511
01512 for (int i = 0; i < nContentHeight; i++)
01513 {
01514 for (int j = 0; j < nContentWidth; j++)
01515 {
01516 int red = *buf++;
01517 int green = *buf++;
01518 int blue = *buf++;
01519 m_image.setPixel(j, i, qRgb(red, green, blue));
01520 }
01521 }
01522 delete outputbuf;
01523 }
01524
01525 Close:
01526 free(buff);
01527 avcodec_close(c);
01528 av_free(c);
01529 av_free(picture);
01530 }
01531
01532
01533 void MHIBitmap::ScaleImage(int newWidth, int newHeight)
01534 {
01535 if (m_image.isNull())
01536 return;
01537
01538 if (newWidth == m_image.width() && newHeight == m_image.height())
01539 return;
01540
01541 if (newWidth <= 0 || newHeight <= 0)
01542 {
01543 m_image.reset();
01544 return;
01545 }
01546
01547 m_image = m_image.smoothScale(newWidth, newHeight);
01548 }
01549
01550