MythUIType::MythUIType(QObject *parent, const QString &name) : QObject(parent) { setObjectName(name); m_Visible = true; m_Enabled = true; m_CanHaveFocus = m_HasFocus = false; m_Area = MythRect(0, 0, 0, 0); m_MinArea = MythRect(0, 0, 0, 0); m_NeedsRedraw = false; m_Alpha = 255; m_AlphaChangeMode = m_AlphaChange = m_AlphaMin = 0; m_AlphaMax = 255; m_Moving = false; m_XYDestination = QPoint(0, 0); m_XYSpeed = QPoint(0, 0); m_deferload = false; m_Parent = NULL; if (parent) { m_Parent = dynamic_cast<MythUIType *>(parent); if (m_Parent) m_Parent->AddChild(this); } m_DirtyRegion = QRegion(QRect(0, 0, 0, 0)); m_Fonts = new FontMap(); m_focusOrder = 0; m_Painter = NULL; }
void TeletextScreen::OptimiseDisplayedArea(void) { VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; MythPainter *osd_painter = vo->GetOSDPainter(); if (!osd_painter) return; QHashIterator<int, QImage*> it(m_rowImages); while (it.hasNext()) { it.next(); MythImage* image = osd_painter->GetFormatImage(); if (!image || !it.value()) continue; int row = it.key(); image->Assign(*(it.value())); MythUIImage *uiimage = new MythUIImage(this, QString("ttrow%1") .arg(row)); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(0, row * m_rowHeight, m_safeArea.width(), m_rowHeight * 2)); } } QRegion visible; QListIterator<MythUIType *> i(m_ChildrenList); while (i.hasNext()) { MythUIType *img = i.next(); visible = visible.united(img->GetArea()); } if (visible.isEmpty()) return; QRect bounding = visible.boundingRect(); bounding = bounding.translated(m_safeArea.topLeft()); bounding = m_safeArea.intersected(bounding); int left = m_safeArea.left() - bounding.left(); int top = m_safeArea.top() - bounding.top(); SetArea(MythRect(bounding)); i.toFront();; while (i.hasNext()) { MythUIType *img = i.next(); img->SetArea(img->GetArea().translated(left, top)); } }
void SubtitleScreen::OptimiseDisplayedArea(void) { if (!m_refreshArea) return; QRegion visible; QListIterator<MythUIType *> i(m_ChildrenList); while (i.hasNext()) { MythUIType *img = i.next(); visible = visible.united(img->GetArea()); } if (visible.isEmpty()) return; QRect bounding = visible.boundingRect(); bounding = bounding.translated(m_safeArea.topLeft()); bounding = m_safeArea.intersected(bounding); int left = m_safeArea.left() - bounding.left(); int top = m_safeArea.top() - bounding.top(); SetArea(MythRect(bounding)); i.toFront();; while (i.hasNext()) { MythUIType *img = i.next(); img->SetArea(img->GetArea().translated(left, top)); } }
// Called by the video player to redraw the image. void MHIContext::UpdateOSD(InteractiveScreen *osdWindow, MythPainter *osdPainter) { if (!osdWindow || !osdPainter) return; QMutexLocker locker(&m_display_lock); m_updated = false; osdWindow->DeleteAllChildren(); // Copy all the display items into the display. list<MHIImageData*>::iterator it = m_display.begin(); for (int count = 0; it != m_display.end(); ++it, count++) { MHIImageData *data = *it; MythImage* image = osdPainter->GetFormatImage(); if (!image) continue; image->Assign(data->m_image); MythUIImage *uiimage = new MythUIImage(osdWindow, QString("itv%1") .arg(count)); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(data->m_x, data->m_y, data->m_image.width(), data->m_image.height())); } image->DecrRef(); } osdWindow->OptimiseDisplayedArea(); // N.B. bypasses OSD class hence no expiry set osdWindow->SetVisible(true); }
/** * \brief Initialises the class */ void MythUIImage::Init(void) { m_cropRect = MythRect(0,0,0,0); m_ForceSize = QSize(0,0); m_CurPos = 0; m_LastDisplay = QTime::currentTime(); m_NeedLoad = false; m_isReflected = false; m_reflectShear = 0; m_reflectScale = m_reflectLength = 100; m_reflectAxis = ReflectVertical; m_reflectSpacing = 0; m_isMasked = false; m_maskImage = NULL; m_isGreyscale = false; m_preserveAspect = false; m_animationCycle = kCycleStart; m_animationReverse = false; m_animatedImage = false; }
void SubtitleScreen::AddScaledImage(QImage &img, QRect &pos) { VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; QRect scaled = vo->GetImageRect(pos); if (scaled.size() != pos.size()) { img = img.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = vo->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); if (image) { image->Assign(img); MythUIImage *uiimage = new MythUIImage(this, "dvd_button"); if (uiimage) { m_refreshArea = true; uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); } } }
/** * \brief Reset the image back to the default defined in the theme */ void MythUIImage::Reset(void) { d->m_UpdateLock.lockForWrite(); SetMinArea(MythRect()); if (m_imageProperties.filename != m_OrigFilename) { m_imageProperties.isThemeImage = true; m_imageProperties.filename = m_OrigFilename; if (m_animatedImage) { m_LowNum = 0; m_HighNum = 0; m_animatedImage = false; } emit DependChanged(true); d->m_UpdateLock.unlock(); Load(); } else d->m_UpdateLock.unlock(); MythUIType::Reset(); }
void SubtitleScreen::DisableForcedSubtitles(void) { if (kDisplayNone != m_subtitleType) return; ClearAllSubtitles(); SetVisible(false); SetArea(MythRect()); }
void MythUITextEdit::SetInitialStates() { if (m_initialized) return; m_initialized = true; m_Text = dynamic_cast<MythUIText*>(GetChild("text")); m_cursorImage = dynamic_cast<MythUIImage*>(GetChild("cursor")); m_backgroundState = dynamic_cast<MythUIStateType*>(GetChild("background")); if (!m_Text) VERBOSE(VB_IMPORTANT, LOC_ERR + "Missing text element."); if (!m_cursorImage) VERBOSE(VB_IMPORTANT, LOC_ERR + "Missing cursor element."); if (!m_backgroundState) VERBOSE(VB_IMPORTANT, LOC_WARN + "Missing background element."); if (!m_Text || !m_cursorImage) { m_Text = NULL; m_cursorImage = NULL; m_backgroundState = NULL; return; } if (m_backgroundState && !m_backgroundState->DisplayState("active")) VERBOSE(VB_IMPORTANT, "MythUITextEdit: active state doesn't exist"); QFontMetrics fm(m_Text->GetFontProperties()->face()); int height = fm.height(); if (height > 0) { MythRect imageArea = m_cursorImage->GetArea(); int width = int(((float)height / (float)imageArea.height()) * (float)imageArea.width()); if (width <= 0) width = 1; m_cursorImage->ForceSize(QSize(width,height)); } MythRect textrect = m_Text->GetArea(); if (textrect.isNull()) textrect = MythRect(5,5,m_Area.width()-10,m_Area.height()-10); textrect.setWidth(textrect.width() - m_cursorImage->GetArea().width()); if (textrect.isValid()) m_Text->SetArea(textrect); m_Text->SetCutDown(false); m_cursorImage->SetPosition(textrect.x(),textrect.y()); }
MythUIType::MythUIType(QObject *parent, const QString &name) : QObject(parent) { setObjectName(name); m_Visible = true; m_Enabled = true; m_EnableInitiator = false; m_Initiator = false; m_Vanish = false; m_Vanished = false; m_CanHaveFocus = m_HasFocus = false; m_Area = MythRect(0, 0, 0, 0); m_MinArea = MythRect(0, 0, 0, 0); m_NeedsRedraw = false; m_AlphaChangeMode = m_AlphaChange = m_AlphaMin = 0; m_AlphaMax = 255; m_Moving = false; m_XYDestination = QPoint(0, 0); m_XYSpeed = QPoint(0, 0); m_deferload = false; m_IsDependDefault = false; m_Parent = nullptr; if (parent) { m_Parent = dynamic_cast<MythUIType *>(parent); if (m_Parent) m_Parent->AddChild(this); } m_DirtyRegion = QRegion(QRect(0, 0, 0, 0)); m_Fonts = new FontMap(); m_focusOrder = 0; m_Painter = nullptr; m_BorderColor = QColor(random() % 255, random() % 255, random() % 255); }
MythRect XMLParseBase::parseRect(const QString &text, bool normalize) { MythRect retval; QStringList values = text.split(',', QString::SkipEmptyParts); if (values.size() == 4) retval = MythRect(values[0], values[1], values[2], values[3]); if (normalize) retval.NormRect(); return retval; }
void InteractiveScreen::UpdateArea(void) { if (m_ChildrenList.isEmpty()) { m_safeArea = QRect(); } else if (m_player && m_player->getVideoOutput()) { float tmp = 0.0; QRect dummy; m_player->getVideoOutput()->GetOSDBounds(dummy, m_safeArea, tmp, tmp, tmp); } SetArea(MythRect(m_safeArea)); }
void SubtitleScreen::EnableSubtitles(int type, bool forced_only) { if (forced_only) { SetVisible(true); SetArea(MythRect()); return; } m_subtitleType = type; if (m_subreader) { m_subreader->EnableAVSubtitles(kDisplayAVSubtitle == m_subtitleType); m_subreader->EnableTextSubtitles(kDisplayTextSubtitle == m_subtitleType); m_subreader->EnableRawTextSubtitles(kDisplayRawTextSubtitle == m_subtitleType); } if (m_608reader) m_608reader->SetEnabled(kDisplayCC608 == m_subtitleType); if (m_708reader) m_708reader->SetEnabled(kDisplayCC708 == m_subtitleType); ClearAllSubtitles(); SetVisible(m_subtitleType != kDisplayNone); SetArea(MythRect()); }
void ImageProperties::Init() { filename = QString(); cropRect = MythRect(0, 0, 0, 0); forceSize = QSize(0, 0); preserveAspect = false; isGreyscale = false; isReflected = false; isMasked = false; isOriented = false; reflectAxis = ReflectVertical; reflectScale = 100; reflectLength = 100; reflectShear = 0; reflectSpacing = 0; orientation = 1; isThemeImage = false; maskImage = NULL; }
void SubtitleScreen::DrawTextSubtitles(QStringList &wrappedsubs, uint64_t start, uint64_t duration) { QFontMetrics font(*(gTextSubFont->GetFace())); int height = font.height() * (1 + PAD_HEIGHT); int pad_width = font.maxWidth() * PAD_WIDTH; int y = m_safeArea.height() - (height * wrappedsubs.size()); int centre = m_safeArea.width() / 2; QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern); foreach (QString subtitle, wrappedsubs) { if (subtitle.isEmpty()) continue; int width = font.width(subtitle) + pad_width * 2; int x = centre - (width / 2) - pad_width; QRect rect(x, y, width, height); if (m_useBackground) { MythUIShape *shape = new MythUIShape(this, QString("tsubbg%1%2").arg(x).arg(y)); shape->SetFillBrush(bgfill); shape->SetArea(MythRect(rect)); if (duration > 0) m_expireTimes.insert(shape, start + duration); } MythUISimpleText* text = new MythUISimpleText (subtitle, *gTextSubFont, rect, Qt::AlignCenter, this, QString("tsub%1%2").arg(x).arg(y)); y += height; LOG(VB_PLAYBACK, LOG_INFO, LOC + subtitle); m_refreshArea = true; if (duration > 0) { m_expireTimes.insert(text, start + duration); LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display text subtitle for %1 ms").arg(duration)); } } }
/*! * \brief Initialise the tree having loaded the formatting options from the * theme */ void MythUIButtonTree::Init() { if (!m_listTemplate) m_listTemplate = dynamic_cast<MythUIButtonList *> (GetChild("listtemplate")); if (!m_listTemplate) { LOG(VB_GENERAL, LOG_ERR, QString("(%1) MythUIButtonList listtemplate " "is required in mythuibuttonlist: %2") .arg(GetXMLLocation().arg(objectName()))); return; } m_listTemplate->SetVisible(false); int width = (m_Area.width() - (m_listSpacing * (m_numLists - 1))) / m_numLists; int height = m_Area.height(); int i = 0; while (i < (int)m_numLists) { QString listname = QString("buttontree list %1").arg(i); MythUIButtonList *list = new MythUIButtonList(this, listname); list->CopyFrom(m_listTemplate); list->SetVisible(false); list->SetActive(false); list->SetCanTakeFocus(false); int x = i * (width + m_listSpacing); MythRect listArea = MythRect(x, 0, width, height); list->SetArea(listArea); m_buttonlists.append(list); i++; } m_initialized = true; }
void BDOverlayScreen::DisplayBDOverlay(BDOverlay *overlay) { if (!overlay || !m_player) return; if (!overlay->m_data) { m_overlayArea = overlay->m_position; SetArea(MythRect(m_overlayArea)); DeleteAllChildren(); m_overlayMap.clear(); SetRedraw(); LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Initialised Size: %1x%2 (%3+%4) Plane: %5 Pts: %6") .arg(overlay->m_position.width()) .arg(overlay->m_position.height()) .arg(overlay->m_position.left()) .arg(overlay->m_position.top()) .arg(overlay->m_plane) .arg(overlay->m_pts)); BDOverlay::DeleteOverlay(overlay); return; } if (!m_overlayArea.isValid()) { LOG(VB_GENERAL, LOG_ERR, LOC + "Error: Overlay image submitted before initialisation."); } VideoOutput *vo = m_player->GetVideoOutput(); if (!vo) return; QRect rect = overlay->m_position; QString hash = QString("%1+%2+%3x%4") .arg(rect.left()).arg(rect.top()) .arg(rect.width()).arg(rect.height()); // remove if we already have this overlay if (m_overlayMap.contains(hash)) { LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Removing %1 (%2 left)") .arg(hash).arg(m_overlayMap.size())); MythUIImage *old = m_overlayMap.take(hash); DeleteChild(old); } // convert the overlay palette to ARGB uint32_t *origpalette = (uint32_t *)(overlay->m_palette); QVector<unsigned int> palette; for (int i = 0; i < 256; i++) { int y = (origpalette[i] >> 0) & 0xff; int cr = (origpalette[i] >> 8) & 0xff; int cb = (origpalette[i] >> 16) & 0xff; int a = (origpalette[i] >> 24) & 0xff; int r = int(y + 1.4022 * (cr - 128)); int b = int(y + 1.7710 * (cb - 128)); int g = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)); if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; if (r > 0xff) r = 0xff; if (g > 0xff) g = 0xff; if (b > 0xff) b = 0xff; palette.push_back((a << 24) | (r << 16) | (g << 8) | b); } // convert the image to QImage QImage img(rect.size(), QImage::Format_Indexed8); memcpy(img.bits(), overlay->m_data, rect.width() * rect.height()); img.setColorTable(palette); img.convertToFormat(QImage::Format_ARGB32); // add to screen QRect scaled = vo->GetImageRect(rect); if (scaled.size() != rect.size()) { img = img.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = vo->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); if (image) { image->Assign(img); MythUIImage *uiimage = new MythUIImage(this, "bdoverlay"); if (uiimage) { uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); m_overlayMap.insert(hash, uiimage); LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Added %1 (%2 tot)") .arg(hash).arg(m_overlayMap.size())); } } SetRedraw(); BDOverlay::DeleteOverlay(overlay); }
/** * \brief Load the image(s), wraps ImageLoader::LoadImage() */ bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat) { d->m_UpdateLock.lockForRead(); m_Initiator = m_EnableInitiator; QString bFilename = m_imageProperties.filename; bFilename.detach(); d->m_UpdateLock.unlock(); QString filename = bFilename; if (bFilename.isEmpty()) { Clear(); SetMinArea(MythRect()); SetRedraw(); return false; } if (getenv("DISABLETHREADEDMYTHUIIMAGE")) allowLoadInBackground = false; // Don't clear the widget before we need to, otherwise it causes // unsightly flashing. We exclude animations for now since that requires a // deeper fix bool isAnimation = (m_HighNum != m_LowNum) || m_animatedImage; if (isAnimation) Clear(); QString imagelabel; int j = 0; for (int i = m_LowNum; i <= m_HighNum && !m_animatedImage; i++) { if (!m_animatedImage && m_HighNum != m_LowNum && bFilename.contains("%1")) filename = bFilename.arg(i); ImageProperties imProps = m_imageProperties; imProps.filename = filename; imagelabel = ImageLoader::GenImageLabel(imProps); // Only load in the background if allowed and the image is // not already in our mem cache int cacheMode = kCacheIgnoreDisk; if (forceStat) cacheMode |= (int)kCacheForceStat; int cacheMode2 = kCacheNormal; if (forceStat) cacheMode2 |= (int)kCacheForceStat; bool do_background_load = false; if (allowLoadInBackground) { MythImage *img = GetMythUI()->LoadCacheImage( filename, imagelabel, GetPainter(), static_cast<ImageCacheMode>(cacheMode)); if (img) img->DecrRef(); else do_background_load = true; } if (do_background_load) { SetMinArea(MythRect()); LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC + QString("Load(), spawning thread to load '%1'").arg(filename)); m_runningThreads++; ImageLoadThread *bImgThread; bImgThread = new ImageLoadThread(this, GetPainter(), imProps, bFilename, i, static_cast<ImageCacheMode>(cacheMode2)); GetMythUI()->GetImageThreadPool()->start(bImgThread, "ImageLoad"); } else { if (!isAnimation && !GetMythUI()->IsImageInCache(imagelabel)) Clear(); // Perform a blocking load LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC + QString("Load(), loading '%1' in foreground").arg(filename)); bool aborted = false; if (ImageLoader::SupportsAnimation(filename)) { AnimationFrames *myFrames; myFrames = ImageLoader::LoadAnimatedImage(GetPainter(), imProps, static_cast<ImageCacheMode>(cacheMode2), this, aborted); // TODO We might want to handle an abort here more gracefully if (aborted) LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated" "image %1 in foreground") .arg(filename)); SetAnimationFrames(*myFrames); delete myFrames; } else { MythImage *image = NULL; image = ImageLoader::LoadImage(GetPainter(), imProps, static_cast<ImageCacheMode>(cacheMode2), this, aborted); // TODO We might want to handle an abort here more gracefully if (aborted) LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated" "image %1 in foreground") .arg(filename)); if (image) { if (m_imageProperties.forceSize.isNull()) SetSize(image->size()); MythRect rect(GetFullArea()); rect.setSize(image->size()); SetMinArea(rect); m_ImagesLock.lock(); m_Images[j] = image; m_ImagesLock.unlock(); SetRedraw(); d->m_UpdateLock.lockForWrite(); m_LastDisplay = QTime::currentTime(); d->m_UpdateLock.unlock(); } else { Reset(); m_ImagesLock.lock(); m_Images[j] = NULL; m_ImagesLock.unlock(); } } } ++j; } return true; }
void SubtitleScreen::DisplayAVSubtitles(void) { if (!m_player || !m_subreader) return; AVSubtitles* subs = m_subreader->GetAVSubtitles(); QMutexLocker lock(&(subs->lock)); if (subs->buffers.empty() && (kDisplayAVSubtitle != m_subtitleType)) return; VideoOutput *videoOut = m_player->GetVideoOutput(); VideoFrame *currentFrame = videoOut ? videoOut->GetLastShownFrame() : NULL; if (!currentFrame || !videoOut) return; float tmp = 0.0; QRect dummy; videoOut->GetOSDBounds(dummy, m_safeArea, tmp, tmp, tmp); while (!subs->buffers.empty()) { const AVSubtitle subtitle = subs->buffers.front(); if (subtitle.start_display_time > currentFrame->timecode) break; ClearDisplayedSubtitles(); subs->buffers.pop_front(); for (std::size_t i = 0; i < subtitle.num_rects; ++i) { AVSubtitleRect* rect = subtitle.rects[i]; bool displaysub = true; if (subs->buffers.size() > 0 && subs->buffers.front().end_display_time < currentFrame->timecode) { displaysub = false; } if (displaysub && rect->type == SUBTITLE_BITMAP) { // AVSubtitleRect's image data's not guaranteed to be 4 byte // aligned. QSize img_size(rect->w, rect->h); QRect img_rect(rect->x, rect->y, rect->w, rect->h); QRect display(rect->display_x, rect->display_y, rect->display_w, rect->display_h); // XSUB and some DVD/DVB subs are based on the original video // size before the video was converted. We need to guess the // original size and allow for the difference int right = rect->x + rect->w; int bottom = rect->y + rect->h; if (subs->fixPosition || (currentFrame->height < bottom) || (currentFrame->width < right)) { int sd_height = 576; if ((m_player->GetFrameRate() > 26.0f) && bottom <= 480) sd_height = 480; int height = ((currentFrame->height <= sd_height) && (bottom <= sd_height)) ? sd_height : ((currentFrame->height <= 720) && bottom <= 720) ? 720 : 1080; int width = ((currentFrame->width <= 720) && (right <= 720)) ? 720 : ((currentFrame->width <= 1280) && (right <= 1280)) ? 1280 : 1920; display = QRect(0, 0, width, height); } QRect scaled = videoOut->GetImageRect(img_rect, &display); QImage qImage(img_size, QImage::Format_ARGB32); for (int y = 0; y < rect->h; ++y) { for (int x = 0; x < rect->w; ++x) { const uint8_t color = rect->pict.data[0][y*rect->pict.linesize[0] + x]; const uint32_t pixel = *((uint32_t*)rect->pict.data[1]+color); qImage.setPixel(x, y, pixel); } } if (scaled.size() != img_size) { qImage = qImage.scaled(scaled.width(), scaled.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } MythPainter *osd_painter = videoOut->GetOSDPainter(); MythImage* image = NULL; if (osd_painter) image = osd_painter->GetFormatImage(); long long displayfor = subtitle.end_display_time - subtitle.start_display_time; if (displayfor == 0) displayfor = 60000; displayfor = (displayfor < 50) ? 50 : displayfor; long long late = currentFrame->timecode - subtitle.start_display_time; MythUIImage *uiimage = NULL; if (image) { image->Assign(qImage); QString name = QString("avsub%1").arg(i); uiimage = new MythUIImage(this, name); if (uiimage) { m_refreshArea = true; uiimage->SetImage(image); uiimage->SetArea(MythRect(scaled)); m_expireTimes.insert(uiimage, currentFrame->timecode + displayfor); } } if (uiimage) { LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display %1AV subtitle for %2ms") .arg(subtitle.forced ? "FORCED " : "") .arg(displayfor)); if (late > 50) LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AV Sub was %1ms late").arg(late)); } } #ifdef USING_LIBASS else if (displaysub && rect->type == SUBTITLE_ASS) { InitialiseAssTrack(m_player->GetDecoder()->GetTrack(kTrackTypeSubtitle)); AddAssEvent(rect->ass); } #endif } m_subreader->FreeAVSubtitle(subtitle); } #ifdef USING_LIBASS RenderAssTrack(currentFrame->timecode); #endif }
void SubtitleScreen::Display708Strings(const CC708Window &win, int num, float aspect, vector<CC708String*> &list) { LOG(VB_VBI, LOG_INFO,LOC + QString("Display Win %1, Anchor_id %2, x_anch %3, y_anch %4, " "relative %5") .arg(num).arg(win.anchor_point).arg(win.anchor_horizontal) .arg(win.anchor_vertical).arg(win.relative_pos)); bool display = false; MythFontProperties *mythfont; uint max_row_width = 0; uint total_height = 0; uint i = 0; for (uint row = 0; (row < win.true_row_count) && (i < list.size()); row++) { uint row_width = 0, max_row_height = 0; for (; (i < list.size()) && list[i] && (list[i]->y <= row); i++) { if (list[i]->y < row) continue; mythfont = Get708Font(list[i]->attr); if (!mythfont) continue; QString text = list[i]->str.trimmed(); if (!text.isEmpty()) display = true; QFontMetrics font(*(mythfont->GetFace())); uint height = (uint)font.height() * (1 + PAD_HEIGHT); row_width += font.width(list[i]->str) + (font.maxWidth() * PAD_WIDTH * 2); max_row_height = max(max_row_height, height); } max_row_width = max(max_row_width, row_width); total_height += max_row_height; } if (!display) return; float xrange = win.relative_pos ? 100.0f : (aspect > 1.4f) ? 210.0f : 160.0f; float yrange = win.relative_pos ? 100.0f : 75.0f; float xmult = (float)m_safeArea.width() / xrange; float ymult = (float)m_safeArea.height() / yrange; uint anchor_x = (uint)(xmult * (float)win.anchor_horizontal); uint anchor_y = (uint)(ymult * (float)win.anchor_vertical); if (win.anchor_point % 3 == 1) anchor_x -= (((int)max_row_width) / 2); if (win.anchor_point % 3 == 2) anchor_x -= (int)max_row_width; if (win.anchor_point / 3 == 1) anchor_y -= (((int)total_height) / 2); if (win.anchor_point / 3 == 2) anchor_y -= (int)total_height; if (win.GetFillAlpha()) // TODO border? { QRect bg(anchor_x, anchor_y, max_row_width, total_height); QBrush fill(win.GetFillColor(), Qt::SolidPattern); MythUIShape *shape = new MythUIShape(this, QString("cc708bg%1").arg(num)); shape->SetFillBrush(fill); shape->SetArea(MythRect(bg)); m_708imageCache[num].append(shape); m_refreshArea = true; } i = 0; int y = anchor_y; for (uint row = 0; (row < win.true_row_count) && (i < list.size()); row++) { uint maxheight = 0; int x = anchor_x; bool first = true; for (; (i < list.size()) && list[i] && (list[i]->y <= row); i++) { bool last = ((i + 1) == list.size()); if (!last) last = (list[i + 1]->y > row); QString rawstring = list[i]->str; mythfont = Get708Font(list[i]->attr); if ((list[i]->y < row) || !mythfont || rawstring.isEmpty()) continue; QString trimmed = rawstring.trimmed(); if (!trimmed.size() && last) continue; QFontMetrics font(*(mythfont->GetFace())); uint height = (uint)font.height() * (1 + PAD_HEIGHT); maxheight = max(maxheight, height); uint spacewidth = font.width(QString(" ")); uint textwidth = font.width(trimmed); int leading = 0; int trailing = 0; if (trimmed.size() != rawstring.size()) { if (trimmed.size()) { leading = rawstring.indexOf(trimmed.at(0)); trailing = rawstring.size() - trimmed.size() - leading; } else { leading = rawstring.size(); } leading *= spacewidth; trailing *= spacewidth; } if (!leading) textwidth += spacewidth * PAD_WIDTH; if (!trailing) textwidth += spacewidth * PAD_WIDTH; bool background = list[i]->attr.GetBGAlpha(); QBrush bgfill = QBrush((list[i]->attr.GetBGColor(), Qt::SolidPattern)); if (leading && background && !first) { // draw background for leading space QRect space(x, y, leading, height); MythUIShape *shape = new MythUIShape(this, QString("cc708shape%1x%2lead").arg(row).arg(i)); shape->SetFillBrush(bgfill); shape->SetArea(MythRect(space)); m_708imageCache[num].append(shape); m_refreshArea = true; } x += leading; QRect rect(x, y, textwidth, height); if (trimmed.size() && textwidth && background) { MythUIShape *shape = new MythUIShape(this, QString("cc708shape%1x%2main").arg(row).arg(i)); shape->SetFillBrush(bgfill); shape->SetArea(MythRect(rect)); m_708imageCache[num].append(shape); m_refreshArea = true; } if (trimmed.size() && textwidth) { MythUIText *text = new MythUIText(list[i]->str, *mythfont, rect, rect, (MythUIType*)this, QString("cc708text%1x%2").arg(row).arg(i)); m_708imageCache[num].append(text); if (text) text->SetJustification(Qt::AlignCenter); m_refreshArea = true; } x += textwidth; if (trailing && background && !last) { // draw background for trailing space QRect space(x, y, trailing, height); MythUIShape *shape = new MythUIShape(this, QString("cc708shape%1x%2trail").arg(row).arg(i)); shape->SetFillBrush(bgfill); shape->SetArea(MythRect(space)); m_708imageCache[num].append(shape); m_refreshArea = true; } x += trailing; first = false; LOG(VB_VBI, LOG_INFO, QString("Win %1 row %2 String '%3'") .arg(num).arg(row).arg(list[i]->str)); } y += maxheight; } }
/** * \brief Crop the image using the given rectangle, useful for removing * unsightly edges from imported images or zoom effects */ void MythUIImage::SetCropRect(int x, int y, int width, int height) { SetCropRect(MythRect(x, y, width, height)); }
void SubtitleScreen::DisplayCC608Subtitles(void) { static const QColor clr[8] = { Qt::white, Qt::red, Qt::green, Qt::yellow, Qt::blue, Qt::magenta, Qt::cyan, Qt::white, }; if (!InitialiseFont(m_fontStretch) || !m_608reader) return; bool changed = false; if (m_player && m_player->GetVideoOutput()) { QRect oldsafe = m_safeArea; m_safeArea = m_player->GetVideoOutput()->GetSafeRect(); if (oldsafe != m_safeArea) changed = true; } else { return; } CC608Buffer* textlist = m_608reader->GetOutputText(changed); if (!changed) return; if (textlist) textlist->lock.lock(); DeleteAllChildren(); if (!textlist) return; if (textlist && textlist->buffers.empty()) { SetRedraw(); textlist->lock.unlock(); return; } vector<CC608Text*>::iterator i = textlist->buffers.begin(); bool teletextmode = (*i)->teletextmode; int xscale = teletextmode ? 40 : 36; int yscale = teletextmode ? 25 : 17; gTextSubFont->GetFace()->setPixelSize(m_safeArea.height() / (yscale * 1.2)); QFontMetrics font(*(gTextSubFont->GetFace())); QBrush bgfill = QBrush(QColor(0, 0, 0), Qt::SolidPattern); int height = font.height() * (1 + PAD_HEIGHT); int pad_width = font.maxWidth() * PAD_WIDTH; for (; i != textlist->buffers.end(); i++) { CC608Text *cc = (*i); if (cc && (cc->text != QString::null)) { int width = font.width(cc->text) + pad_width; int x = teletextmode ? cc->y : (cc->x + 3); int y = teletextmode ? cc->x : cc->y; x = (int)(((float)x / (float)xscale) * (float)m_safeArea.width()); y = (int)(((float)y / (float)yscale) * (float)m_safeArea.height()); QRect rect(x, y, width, height); if (!teletextmode && m_useBackground) { MythUIShape *shape = new MythUIShape(this, QString("cc608bg%1%2%3").arg(cc->x).arg(cc->y).arg(width)); shape->SetFillBrush(bgfill); QRect bgrect(x - pad_width, y, width + pad_width, height); shape->SetArea(MythRect(bgrect)); } gTextSubFont->SetColor(clr[min(max(0, cc->color), 7)]); MythUIText *text = new MythUIText( cc->text, *gTextSubFont, rect, rect, (MythUIType*)this, QString("cc608txt%1%2%3").arg(cc->x).arg(cc->y).arg(width)); if (text) text->SetJustification(Qt::AlignLeft); m_refreshArea = true; LOG(VB_VBI, LOG_INFO, QString("x %1 y %2 String: '%3'") .arg(cc->x).arg(cc->y).arg(cc->text)); } } textlist->lock.unlock(); }