Ticket #12057: 12057_v2.patch

File 12057_v2.patch, 37.6 KB (added by Jim Stichnoth, 9 years ago)

Updated to latest master

  • mythtv/libs/libmythtv/subtitlescreen.cpp

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.cpp b/mythtv/libs/libmythtv/subtitlescreen.cpp
    index afbe759..3dce6ae 100644
    a b public: 
    7272    MythImage *GetImage(void) { return m_Images[0]; }
    7373};
    7474
     75// Used to lookup fonts by family, attributes, zoom and stretch factors.
     76class SubtitleFontKey
     77{
     78    public:
     79        SubtitleFontKey(const QString &family,
     80                        const CC708CharacterAttribute &attr,
     81                        int zoom, int stretch)
     82            : m_family(family), m_attr(attr), m_zoom(zoom), m_stretch(stretch)
     83            {}
     84        ~SubtitleFontKey(void) {}
     85
     86        QString m_family;
     87        CC708CharacterAttribute m_attr;
     88        int m_zoom;
     89        int m_stretch;
     90
     91        bool operator==(const SubtitleFontKey &other) const;
     92};
     93
     94bool
     95SubtitleFontKey::operator==(const SubtitleFontKey &other) const
     96{
     97    return
     98        (m_family              == other.m_family &&
     99        m_attr.pen_size        == other.m_attr.pen_size &&
     100        m_attr.offset          == other.m_attr.offset &&
     101        m_attr.text_tag        == other.m_attr.text_tag &&
     102        m_attr.font_tag        == other.m_attr.font_tag &&
     103        m_attr.edge_type       == other.m_attr.edge_type &&
     104        m_attr.underline       == other.m_attr.underline &&
     105        m_attr.italics         == other.m_attr.italics &&
     106        m_attr.boldface        == other.m_attr.boldface &&
     107        m_attr.fg_color        == other.m_attr.fg_color &&
     108        m_attr.fg_opacity      == other.m_attr.fg_opacity &&
     109        m_attr.bg_color        == other.m_attr.bg_color &&
     110        m_attr.bg_opacity      == other.m_attr.bg_opacity &&
     111        m_attr.edge_color      == other.m_attr.edge_color &&
     112        m_attr.actual_fg_color == other.m_attr.actual_fg_color &&
     113        m_zoom                 == other.m_zoom &&
     114        m_stretch              == other.m_stretch);
     115}
     116
     117uint qHash(const SubtitleFontKey &key);
     118
     119uint
     120qHash(const SubtitleFontKey &key)
     121{
     122    return qHash(key.m_family) +
     123        key.m_attr.pen_size +
     124        key.m_attr.offset +
     125        key.m_attr.text_tag +
     126        key.m_attr.font_tag +
     127        key.m_attr.edge_type +
     128        key.m_attr.underline +
     129        key.m_attr.italics +
     130        key.m_attr.boldface +
     131        key.m_attr.fg_color +
     132        key.m_attr.fg_opacity +
     133        key.m_attr.bg_color +
     134        key.m_attr.bg_opacity +
     135        key.m_attr.edge_color +
     136        qHash(key.m_attr.actual_fg_color.name()) +
     137        key.m_zoom +
     138        key.m_stretch;
     139}
     140
    75141////////////////////////////////////////////////////////////////////////////
    76142
    77143// Class SubtitleFormat manages fonts and backgrounds for subtitles.
    public: 
    130196class SubtitleFormat
    131197{
    132198public:
    133     SubtitleFormat(void) {}
     199    SubtitleFormat(SubtitleScreen* screen = 0,
     200                   MythPlayer *player = 0,
     201                   int stretch = 100);
    134202    ~SubtitleFormat(void);
    135203    MythFontProperties *GetFont(const QString &family,
    136204                                const CC708CharacterAttribute &attr,
    137                                 int pixelSize, int zoom, int stretch);
     205                                int zoom);
    138206    SubShape *GetBackground(MythUIType *parent, const QString &name,
    139207                            const QString &family,
    140208                            const CC708CharacterAttribute &attr,
    public: 
    144212    int GetBackgroundAlpha(const QString &family);
    145213    static QString MakePrefix(const QString &family,
    146214                              const CC708CharacterAttribute &attr);
     215
     216    // Temporary method until teletextscreen.cpp is refactored into
     217    // subtitlescreen.cpp
     218    // GetTeletextFontName is the only public method that should be called
     219    // when m_subScreen and m_player are null
     220    QString GetTeletextFontName(void);
     221
    147222private:
    148223    void Load(const QString &family,
    149224              const CC708CharacterAttribute &attr);
    private: 
    165240                              MythUIShape *bg1,
    166241                              MythUIShape *bg2);
    167242
     243    // Hash table of default font properties by family
    168244    QHash<QString, MythFontProperties *> m_fontMap;
    169245    QHash<QString, MythUIShape *>       m_shapeMap;
    170246    QHash<QString, QSet<QString> >     m_changeMap;
    private: 
    174250    QHash<QString, int>             m_pixelSizeMap;
    175251    QHash<QString, int>           m_outlineSizeMap;
    176252    QHash<QString, QPoint>       m_shadowOffsetMap;
     253
     254    // Hash table of font properties by family, atttributes, zoom & stretch
     255    QHash<SubtitleFontKey, MythFontProperties *> m_fullFontMap;
     256
    177257    QVector<MythUIType *> m_cleanup;
     258    SubtitleScreen* m_subScreen;
     259    MythPlayer* m_player;
     260    int m_fontStretch;
    178261};
    179262
    180263static const QString kSubProvider("provider");
    static QString fontToString(MythFontProperties *f) 
    244327MythFontProperties *
    245328SubtitleFormat::GetFont(const QString &family,
    246329                        const CC708CharacterAttribute &attr,
    247                         int pixelSize, int zoom, int stretch)
     330                        int zoom)
    248331{
    249     int origPixelSize = pixelSize;
     332    QString prefix = MakePrefix(family, attr);
     333    SubtitleFontKey key(prefix, attr, zoom, m_fontStretch);
     334    if (m_fullFontMap.contains(key))
     335        return m_fullFontMap[key];
     336
     337    if (!m_fontMap.contains(prefix))
     338        Load(family, attr);
     339
     340    // Make a copy we can modify once and reuse many times
     341    MythFontProperties *result = new MythFontProperties(*(m_fontMap[prefix]));
     342    m_fullFontMap[key] = result;
     343
    250344    float scale = zoom / 100.0;
    251345    if ((attr.pen_size & 0x3) == k708AttrSizeSmall)
    252346        scale = scale * 32 / 42;
    253347    else if ((attr.pen_size & 0x3) == k708AttrSizeLarge)
    254348        scale = scale * 42 / 32;
    255349
    256     QString prefix = MakePrefix(family, attr);
    257     if (!m_fontMap.contains(prefix))
    258         Load(family, attr);
    259     MythFontProperties *result = m_fontMap[prefix];
    260 
    261350    // Apply the scaling factor to pixelSize even if the theme
    262351    // explicitly sets pixelSize.
    263     if (!IsUnlocked(prefix, kSubAttrPixelsize))
    264         pixelSize = m_pixelSizeMap[prefix];
     352    int pixelSize = m_pixelSizeMap[prefix];
     353    int origPixelSize = pixelSize;
    265354    pixelSize *= scale;
    266355    result->GetFace()->setPixelSize(pixelSize);
    267356
    268     result->GetFace()->setStretch(stretch);
     357    // Stretch is now set in SubtitleFormat::Load
     358    // result->GetFace()->setStretch(stretch);
     359
    269360    if (IsUnlocked(prefix, kSubAttrItalics))
    270361        result->GetFace()->setItalic(attr.italics);
    271362    if (IsUnlocked(prefix, kSubAttrUnderline))
    SubtitleFormat::GetFont(const QString &family, 
    301392    else
    302393    {
    303394        offset = m_shadowOffsetMap[prefix];
    304         offset.NormPoint();
     395        // Shadow offset has already been scaled from theme to screen
     396        // Why do it again?
     397        // offset.NormPoint();
    305398        offset.setX(offset.x() * scale + 0.5);
    306399        offset.setY(offset.y() * scale + 0.5);
    307400    }
    SubtitleFormat::GetFont(const QString &family, 
    329422    else
    330423    {
    331424        off = m_outlineSizeMap[prefix];
    332         MythPoint point(off, off);
    333         point.NormPoint();
    334         off = point.x() * scale + 0.5;
     425        // Outline has already been scaled by SubtitleFormat::Load.
     426        // Why do it again?
     427        // MythPoint point(off, off);
     428        // point.NormPoint();
     429        off = off * scale + 0.5;
    335430    }
    336431    result->SetOutline(outline, color, off, alpha);
    337432
    338433    LOG(VB_VBI, LOG_DEBUG,
    339434        QString("GetFont(family=%1, prefix=%2, orig pixelSize=%3, "
    340                 "new pixelSize=%4 zoom=%5) = %6")
    341         .arg(family).arg(prefix).arg(origPixelSize).arg(pixelSize)
    342         .arg(zoom).arg(fontToString(result)));
     435                "zoom=%4, stretch=%5) = %6")
     436        .arg(family).arg(prefix).arg(origPixelSize)
     437        .arg(zoom).arg(m_fontStretch).arg(fontToString(result)));
    343438    return result;
    344439}
    345440
     441QString SubtitleFormat::GetTeletextFontName(void)
     442{
     443    CC708CharacterAttribute attr(false, false, false, Qt::white);
     444
     445    QString prefix = MakePrefix(kSubFamilyTeletext, attr);
     446    if (!m_fontMap.contains(prefix))
     447        Load(kSubFamilyTeletext, attr);
     448    MythFontProperties *result = m_fontMap[prefix];
     449    return result->face().family();
     450}
     451
     452SubtitleFormat::SubtitleFormat(SubtitleScreen* screen, MythPlayer* player,
     453                               int stretch) :
     454    m_subScreen(screen), m_player(player), m_fontStretch(stretch)
     455{
     456}
     457
    346458SubtitleFormat::~SubtitleFormat(void)
    347459{
     460    QHash<SubtitleFontKey, MythFontProperties*>::const_iterator it;
     461    for (it = m_fullFontMap.constBegin(); it !=  m_fullFontMap.constEnd(); it++)
     462    {
     463        delete (*it);
     464    }
     465
    348466    for (int i = 0; i < m_cleanup.size(); ++i)
    349467    {
    350468        m_cleanup[i]->DeleteAllChildren();
    void SubtitleFormat::CreateProviderDefault(const QString &family, 
    412530
    413531    if (isComplement)
    414532        Complement(font, bg);
    415     parent->AddFont(kSubProvider, font);
    416533
    417     *returnFont = font;
     534    // This ultimately calls FontMap::AddFont, which makes a deep copy
     535    // so we need to delete font.
     536    // In case something changes underneath, we make sure the value
     537    // returned by parent->GetFont is indeed different.
     538    parent->AddFont(kSubProvider, font);
     539    *returnFont = parent->GetFont(kSubProvider);
     540    if (*returnFont != font)
     541        delete font;
    418542    *returnBg = bg;
    419543}
    420544
    void SubtitleFormat::Load(const QString &family, 
    511635    QPoint offset;
    512636    QColor color;
    513637    int alpha;
    514     int size;
    515638    resultFont->GetShadow(offset, color, alpha);
     639    m_shadowOffsetMap[prefix] = offset;
     640
     641    // Shadow offset is scaled from theme to screen when it is parsed.
     642    // But outline size is not.
     643    // As long as this inconsistency exists, we scale outline size here.
     644    int size;
    516645    resultFont->GetOutline(color, size, alpha);
     646    MythPoint outline(size, size);
     647    outline.NormPoint();
     648    size = outline.x();
    517649    m_outlineSizeMap[prefix] = size;
    518     m_shadowOffsetMap[prefix] = offset;
    519     m_pixelSizeMap[prefix] = resultFont->GetFace()->pixelSize();
    520650
    521     delete negFont;
     651    // Fixed memory leak in SubtitleFormat::CreateProviderDefault,
     652    // so negFont shouldn't be deleted here.
     653    // It will be deleted later when negParent is deleted via m_cleanup
     654    // delete negFont;
     655
     656    // Font stretch is constant for the life of each SubtitleScreen instance.
     657    // So the same is true for SubtitleFormat.
     658    // It is 100 for most playback profiles.
     659    // But the Slim profile changes it whenever the video zoom factor changes.
     660    // When that happens, the current SubtitleScreen is deleted
     661    // and a new SubtitleScreen with a new font stretch is instantiated.
     662    resultFont->GetFace()->setStretch(m_fontStretch);
     663
     664    // If the user hasn't defined a pixelsize
     665    // and not called via teletextscreen.cpp (where m_subScreen is null),
     666    // set a default
     667    if (IsUnlocked(prefix, kSubAttrPixelsize) && m_subScreen != 0)
     668    {
     669        QRect safeArea = m_subScreen->GetSafeArea();
     670        LOG(VB_VBI, LOG_DEBUG,
     671            QString("safeArea=%1x%2@%3,%4")
     672            .arg(safeArea.width())
     673            .arg(safeArea.height())
     674            .arg(safeArea.x())
     675            .arg(safeArea.y()));
     676
     677        // Start with 608 row,col dimensions
     678        int cols = 32;    // 608 max columns
     679        int rows = 15;    // 608 max rows
     680
     681        // Adjust by family
     682        if (kSubFamily708 == family || kSubFamilyText == family)
     683        {
     684            // Assumption: .srt and .txt line lengths will be within
     685            // the limits of 708 captions, whose # of columns depends on
     686            // the source video's aspect ratio.
     687            cols = (m_player->GetVideoAspect() > 1.4f) ? 42 : 32;
     688            rows = 15;
     689        }
     690        //else if (kSubFamilyAV == family)
     691        //{
     692        //    DVB bitmapped subtitles?
     693        //}
     694        else if (kSubFamilyTeletext == family)
     695        {
     696            // Constants defined in teletextscreen.cpp
     697            // Copied values rather than depend on something that
     698            // going to be refactored?
     699            cols = 40; // TeletextScreen::kTeletextColumns
     700            rows = 26; // TeletextScreen::kTeletextRows
     701        }
     702
     703        // Add one to cols to account for 1/2 char width left&right padding.
     704        cols++;
     705
     706        // Target lineHeight
     707        int lineHeight = safeArea.height() / rows;
     708
     709        // Set pixelSize to the target lineHeight
     710        int pixelSize = lineHeight;
     711        // Reduce by shadow offset or outline size
     712        pixelSize -= max(abs(offset.y()), size);
     713        // It is a first guess pixelSize, which will almost certainly be off
     714
     715        QFont *font = resultFont->GetFace();
     716        font->setPixelSize(pixelSize);
     717        QFontMetrics fm(*font);
     718
     719        // Font metrics for various fonts are inconsistent in how
     720        // they provide a realistic line height value.
     721        // The max of what they call height and lineSpacing
     722        // provides a resonable value for most fonts.
     723        int newLineHeight = max(fm.height(), fm.lineSpacing());
     724        // Increase by shadow offset or outline size
     725        newLineHeight += max(abs(offset.y()), size);
     726
     727        int offBy = newLineHeight - lineHeight;
     728        int newPixelSize = pixelSize;
     729        if (offBy != 0)
     730        {
     731            // We tried a pixelSize equal to the desired line height
     732            // with shadow & outline adjustments,
     733            // but it didn't quite get us there.
     734            // We make a better guess by applying a ratio of
     735            // our first guess divided by the actual height.
     736            // This should be "good enough" for most fonts.
     737            newPixelSize = (int)((float)pixelSize*pixelSize/newLineHeight + 0.5);
     738            font->setPixelSize(newPixelSize);
     739            fm = QFontMetrics(*font);
     740            newLineHeight = max(fm.height(), fm.lineSpacing());
     741            newLineHeight += max(abs(offset.y()), size);
     742        }
     743
     744        int fontwidth = fm.averageCharWidth();
     745        int maxwidth = (safeArea.width() - max(abs(offset.x()), size)) / cols;
     746
     747        LOG(VB_VBI, LOG_DEBUG,
     748            QString("family=%1 default-pixelSize=%2->%3 lineHeight=%4->%5 "
     749                    "offBy=%6 fontwidth=%7 maxwidth=%8 shadow-offset=%9,%10 "
     750                    "outline=%11")
     751            .arg(family).arg(pixelSize).arg(newPixelSize)
     752            .arg(lineHeight).arg(newLineHeight)
     753            .arg(offBy).arg(fontwidth).arg(maxwidth)
     754            .arg(offset.x()).arg(offset.y()).arg(size));
     755
     756        // Check if fontwidth exceeds maxwidth
     757        // Just log it for now. We'll deal with it, if/when someone
     758        // reports a problem.
     759        if (fontwidth > maxwidth)
     760        {
     761            LOG(VB_GENERAL, LOG_WARNING,
     762                QString("fontwidth=%1 exceeds maxwidth=%2")
     763                .arg(fontwidth).arg(maxwidth));
     764        }
     765
     766        m_pixelSizeMap[prefix] = newPixelSize;
     767    }
     768    else
     769    {
     770        int pixelSize = resultFont->GetFace()->pixelSize();
     771        LOG(VB_VBI, LOG_DEBUG,
     772            QString("family=%1 user defined pixelSize=%2")
     773            .arg(family).arg(pixelSize));
     774        m_pixelSizeMap[prefix] = pixelSize;
     775    }
    522776}
    523777
    524778QSet<QString> SubtitleFormat::Diff(const QString &family,
    int SubtitleFormat::GetBackgroundAlpha(const QString &family) 
    637891
    638892////////////////////////////////////////////////////////////////////////////
    639893
    640 QSize FormattedTextChunk::CalcSize(float layoutSpacing) const
     894QSize FormattedTextChunk::CalcSize(float layoutSpacing)
    641895{
    642     return parent->CalcTextSize(text, m_format, layoutSpacing);
     896    if (!textFont)
     897        textFont = parent->GetFont(m_format);
     898    return parent->CalcTextSize(text, m_format, layoutSpacing, textFont);
    643899}
    644900
    645901void FormattedTextChunk::CalcPadding(bool isFirst, bool isLast,
    646                                      int &left, int &right) const
     902                                     int &left, int &right)
    647903{
    648     parent->CalcPadding(m_format, isFirst, isLast, left, right);
     904    if (!textFont)
     905        textFont = parent->GetFont(m_format);
     906    parent->CalcPadding(m_format, isFirst, isLast, left, right, textFont);
    649907}
    650908
    651909bool FormattedTextChunk::Split(FormattedTextChunk &newChunk)
    QString FormattedTextChunk::ToLogString(void) const 
    695953bool FormattedTextChunk::PreRender(bool isFirst, bool isLast,
    696954                                   int &x, int y, int height)
    697955{
    698     textFont = parent->GetFont(m_format);
    699956    if (!textFont)
    700         return false;
     957        textFont = parent->GetFont(m_format);
     958
    701959    QFontMetrics font(*(textFont->GetFace()));
    702960    // If the chunk starts with whitespace, the leading whitespace
    703961    // ultimately gets lost due to the text.trimmed() operation in the
    bool FormattedTextChunk::PreRender(bool isFirst, bool isLast, 
    712970    int leftPadding, rightPadding;
    713971    CalcPadding(isFirst, isLast, leftPadding, rightPadding);
    714972    // Account for extra padding before the first chunk.
    715     if (isFirst)
    716         x += leftPadding;
     973    // No, this will cause everything to be off center to the right
     974    // by the value of leftPadding.
     975    // if (isFirst)
     976    //     x += leftPadding;
    717977    QSize chunk_sz = CalcSize();
    718978    QRect bgrect(x - leftPadding, y,
    719979                 chunk_sz.width() + leftPadding + rightPadding,
    bool FormattedTextChunk::PreRender(bool isFirst, bool isLast, 
    732992    textRect = QRect(x + x_adjust, y,
    733993                     chunk_sz.width() - x_adjust + rightPadding, height);
    734994
     995    // If this chunk is in italics, add a few pixels to the width.
     996    // This is to compensate for some font/char combos that would otherwise
     997    // have the top right corner of the last char clipped.
     998    // The effect can be seen with Droid Sans Mono and certain upper case chars
     999    // like E or W.
     1000    // But don't add this padding to the next chunk's x coordinate.
     1001    // That would add too much spacing between chunks.
     1002    if (m_format.italics)
     1003    {
     1004        int oldWidth = textRect.width();
     1005        textRect.setWidth(oldWidth + font.averageCharWidth() / 2);
     1006        LOG(VB_VBI, LOG_DEBUG,
     1007            QString("Pad for italics width %1 -> %2")
     1008            .arg(oldWidth).arg(textRect.width()));
     1009    }
     1010
    7351011    x += chunk_sz.width();
    7361012    return true;
    7371013}
    7381014
    739 QSize FormattedTextLine::CalcSize(float layoutSpacing) const
     1015QSize FormattedTextLine::CalcSize(float layoutSpacing, bool addPadding)
    7401016{
    7411017    int height = 0, width = 0;
    7421018    int leftPadding = 0, rightPadding = 0;
    743     QList<FormattedTextChunk>::const_iterator it;
    744     bool isFirst = true;
    745     for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
     1019    QList<FormattedTextChunk>::iterator it;
     1020    bool isFirst = (addPadding ? true : false);
     1021    for (it = chunks.begin(); it != chunks.end(); ++it)
    7461022    {
    747         bool isLast = (it + 1 == chunks.constEnd());
     1023        bool isLast = (addPadding ? (it + 1 == chunks.constEnd()) : false);
    7481024        QSize tmp = (*it).CalcSize(layoutSpacing);
    7491025        height = max(height, tmp.height());
    7501026        width += tmp.width();
    751         (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
     1027        // We don't need to call CalcPadding if addPadding is false
     1028        if (addPadding)
     1029            (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
    7521030        if (it == chunks.constBegin())
    7531031            width += leftPadding;
    7541032        isFirst = false;
    FormattedTextSubtitle::FormattedTextSubtitle(void) : 
    7831061// Normal font height is designed to be 1/20 of safe area height, with
    7841062// extra blank space between lines to make 17 lines within the safe
    7851063// area.
    786 static const float LINE_SPACING = (20.0 / 17.0);
     1064// static const float LINE_SPACING = (20.0 / 17.0);
     1065static const float LINE_SPACING = 0.0;
    7871066static const float PAD_WIDTH  = 0.5;
    788 static const float PAD_HEIGHT = 0.04;
     1067// static const float PAD_HEIGHT = 0.04;
    7891068
    7901069// Resolves any TBD x_indent and y_indent values in FormattedTextLine
    7911070// objects.  Calculates m_bounds.  Prunes most leading and all
    void FormattedTextSubtitle::Layout(void) 
    7981077    int anchor_height = 0;
    7991078    for (int i = 0; i < m_lines.size(); i++)
    8001079    {
    801         QSize sz = m_lines[i].CalcSize(LINE_SPACING);
     1080        // We don't want padding added at this point.
     1081        // Padding applies to the background.
     1082        // We want to know where the txt should be drawn,
     1083        // not where the background shoould be drawn.
     1084        QSize sz = m_lines[i].CalcSize(LINE_SPACING, false);
    8021085        anchor_width = max(anchor_width, sz.width());
    8031086        anchor_height += sz.height();
    8041087    }
    void FormattedTextSubtitle::Layout(void) 
    8191102    anchor_y = max(0, min(anchor_y, m_safeArea.height() - anchor_height));
    8201103    anchor_x = max(0, min(anchor_x, m_safeArea.width() - anchor_width));
    8211104
    822     m_bounds = QRect(anchor_x, anchor_y, anchor_width, anchor_height);
     1105    // m_bounds needs padding.
     1106    // Get it using the first chunk of the first line.
     1107    int left = 0;
     1108    int right = 0;
     1109    m_lines[0].chunks.first().CalcPadding(true, true, left, right);
     1110    m_bounds = QRect(anchor_x-left,
     1111                     anchor_y,
     1112                     anchor_width+left+right,
     1113                     anchor_height);
    8231114
    8241115    // Fill in missing coordinates
    8251116    int y = anchor_y;
    void FormattedTextSubtitle::Layout(void) 
    8291120            m_lines[i].x_indent = anchor_x;
    8301121        if (m_lines[i].y_indent < 0)
    8311122            m_lines[i].y_indent = y;
    832         y += m_lines[i].CalcSize(LINE_SPACING).height();
     1123        y += m_lines[i].CalcSize(LINE_SPACING, false).height();
    8331124        // Prune leading all-whitespace chunks.
    8341125        while (!m_lines[i].chunks.isEmpty() &&
    8351126               m_lines[i].chunks.first().text.trimmed().isEmpty())
    void FormattedTextSubtitle::PreRender(void) 
    8641155    {
    8651156        int x = m_lines[i].x_indent;
    8661157        int y = m_lines[i].y_indent;
    867         int height = m_lines[i].CalcSize().height();
     1158        int height = m_lines[i].CalcSize(LINE_SPACING, false).height();
    8681159        QList<FormattedTextChunk>::iterator chunk;
    8691160        bool isFirst = true;
    8701161        for (chunk = m_lines[i].chunks.begin();
    void FormattedTextSubtitle::Draw(void) 
    8931184        {
    8941185            MythFontProperties *mythfont =
    8951186                m_subScreen->GetFont((*chunk).m_format);
    896             if (!mythfont)
    897                 continue;
     1187            // Never returns NULL
     1188            // if (!mythfont)
     1189            //     continue;
    8981190            // Note: NULL is passed as the parent argument to the
    8991191            // MythUI constructors so that we can control the drawing
    9001192            // order of the children.  In particular, background
    9011193            // shapes should be added/drawn first, and text drawn on
    9021194            // top.
     1195            LOG(VB_VBI, LOG_DEBUG,
     1196                QString("i=%1 rect=%2x%3@%4,%5 bgrect=%6x%7@%8,%9 text=%10")
     1197                .arg(i)
     1198                .arg(chunk->textRect.width())
     1199                .arg(chunk->textRect.height())
     1200                .arg(chunk->textRect.x())
     1201                .arg(chunk->textRect.y())
     1202                .arg(chunk->bgShapeRect.width())
     1203                .arg(chunk->bgShapeRect.height())
     1204                .arg(chunk->bgShapeRect.x())
     1205                .arg(chunk->bgShapeRect.y())
     1206                .arg(chunk->text));
    9031207            if ((*chunk).textRect.width() > 0) {
    9041208                SubSimpleText *text =
    9051209                    new SubSimpleText((*chunk).text, *mythfont,
    void FormattedTextSubtitleSRT::Init(const QStringList &subs) 
    9941298    // <font color="#xxyyzz"> - change font color
    9951299    // </font> - reset font color to white
    9961300
    997     int pixelSize = m_safeArea.height() / 20;
    998     if (m_subScreen)
    999         m_subScreen->SetFontSize(pixelSize);
    10001301    m_xAnchorPoint = 1; // center
    10011302    m_yAnchorPoint = 2; // bottom
    10021303    m_xAnchor = m_safeArea.width() / 2;
    void FormattedTextSubtitle608::Layout(void) 
    11971498        int y = m_lines[i].y_indent;
    11981499        if (i == 0)
    11991500            firstY = prevY = y;
    1200         int height = m_lines[i].CalcSize().height();
     1501        int height = m_lines[i].CalcSize(LINE_SPACING, false).height();
    12011502        heights[i] = height;
    12021503        spaceBefore[i] = y - prevY;
    12031504        totalSpace += (y - prevY);
    void FormattedTextSubtitle608::Layout(void) 
    12181519            m_lines[i].y_indent = prevY + spaceBefore[i] * shrink;
    12191520            prevY = m_lines[i].y_indent + heights[i];
    12201521        }
     1522        LOG(VB_VBI, LOG_DEBUG,
     1523            QString("Shrink overage=%1 totalSpace=%2 shrink=%3 firstY=%4 prevY=%5")
     1524            .arg(overage).arg(totalSpace).arg(shrink).arg(firstY).arg(prevY));
    12211525    }
    12221526
    12231527    // Shift Y coordinates back up into the safe area.
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12391543    if (buffers.empty())
    12401544        return;
    12411545    vector<CC608Text*>::const_iterator i = buffers.begin();
    1242     int xscale = 36;
    1243     int yscale = 17;
    1244     int pixelSize = m_safeArea.height() / (yscale * LINE_SPACING);
     1546
     1547    int xscale = 32;    // 608 max columns
     1548    int yscale = 15;    // 608 max rows
     1549
     1550    int lineHeight = m_safeArea.height() / yscale;
    12451551    int fontwidth = 0;
    12461552    int xmid = 0;
    1247     int zoom = 100;
     1553    // int zoom = 100;
    12481554    if (m_subScreen)
    12491555    {
    1250         zoom = m_subScreen->GetZoom();
    1251         m_subScreen->SetFontSize(pixelSize);
     1556        // zoom = m_subScreen->GetZoom();
    12521557        CC708CharacterAttribute def_attr(false, false, false, clr[0]);
    1253         QFont *font = m_subScreen->GetFont(def_attr)->GetFace();
     1558        MythFontProperties* mythfont = m_subScreen->GetFont(def_attr);
     1559        QFont *font = mythfont->GetFace();
    12541560        QFontMetrics fm(*font);
    12551561        fontwidth = fm.averageCharWidth();
    12561562        xmid = m_safeArea.width() / 2;
     1563
     1564        // Font metrics for various fonts are inconsistent in how
     1565        // they provide a realistic line height value.
     1566        // The max of what they call height and lineSpacing
     1567        // provides a resonable value for most fonts.
     1568        lineHeight = max(fm.height(), fm.lineSpacing());
     1569
     1570        // Increase by shadow offset and/or outline size
     1571        QPoint offset;
     1572        QColor color;
     1573        int alpha;
     1574        mythfont->GetShadow(offset, color, alpha);
     1575
     1576        int size;
     1577        mythfont->GetOutline(color, size, alpha);
     1578
     1579        lineHeight += max(abs(offset.y()), size);
     1580
    12571581        // Disable centering for zoom factor >= 100%
    1258         if (zoom >= 100)
    1259             xscale = m_safeArea.width() / fontwidth;
     1582        // Why?? If disabled, 608 captions are shifted to left unless
     1583        // the user sets the zoom to 99% or less.
     1584        // The Layout method should shift left-right and up-down
     1585        // as needed for zoom factor.
     1586        // If zoom factor is too large, the user can reduce it.
     1587        // If they don't we'll clip what doesn't fit.
     1588        //if (zoom >= 100)
     1589        //    xscale = m_safeArea.width() / fontwidth;
    12601590    }
    12611591
    12621592    for (; i != buffers.end(); ++i)
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12671597        const bool isBold = false;
    12681598        QString text(cc->text);
    12691599
     1600        // Note: cc-x (and orig_x) is column # ranging from 0 through 31,
    12701601        int orig_x = cc->x;
    12711602        // position as if we use a fixed size font
    12721603        // - font size already has zoom factor applied
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12791610            // fallback
    12801611            x = (orig_x + 3) * m_safeArea.width() / xscale;
    12811612
     1613        // Note: cc-y (and orig_y) is row # ranging from 1 through 15,
     1614        // we need zero based rows
    12821615        int orig_y = cc->y;
    12831616        int y;
    1284         if (orig_y < yscale / 2)
     1617        if (orig_y <= yscale / 2)
    12851618            // top half -- anchor up
    1286             y = (orig_y * m_safeArea.height() * zoom / (yscale * 100));
     1619            y = (orig_y-1) * lineHeight;
    12871620        else
    12881621            // bottom half -- anchor down
    1289             y = m_safeArea.height() -
    1290                 ((yscale - orig_y - 0.5) * m_safeArea.height() * zoom /
    1291                  (yscale * 100));
     1622            y = m_safeArea.height() - ((yscale - (orig_y-1)) * lineHeight);
    12921623
    12931624        FormattedTextLine line(x, y, orig_x, orig_y);
    12941625        while (!text.isNull())
    void FormattedTextSubtitle708::Init(const CC708Window &win, 
    13341665                "relative %5")
    13351666            .arg(m_num).arg(win.anchor_point).arg(win.anchor_horizontal)
    13361667            .arg(win.anchor_vertical).arg(win.relative_pos));
    1337     int pixelSize = m_safeArea.height() / 20;
    1338     if (m_subScreen)
    1339         m_subScreen->SetFontSize(pixelSize);
    13401668
    13411669    float xrange  = win.relative_pos ? 100.0f :
    13421670                    (aspect > 1.4f) ? 210.0f : 160.0f;
    13431671    float yrange  = win.relative_pos ? 100.0f : 75.0f;
    1344     float xmult   = (float)m_safeArea.width() / xrange;
     1672
     1673    // Center the window addressable area based on the first string's font.
     1674    // Assumption: Most caption providers do not change font within a window
     1675    MythFontProperties* mythFont = m_subScreen->GetFont(list[0]->attr);
     1676    QFont* font = mythFont->GetFace();
     1677    QFontMetrics fm(*font);
     1678    int cols = (aspect > 1.4f) ? 42 : 32;
     1679    int centerWidth = min(m_safeArea.width(), cols * fm.averageCharWidth());
     1680    int xoffset = (m_safeArea.width() - centerWidth) / 2;
     1681
     1682    float xmult   = (float)centerWidth / xrange;
    13451683    float ymult   = (float)m_safeArea.height() / yrange;
    13461684    uint anchor_x = (uint)(xmult * (float)win.anchor_horizontal);
    13471685    uint anchor_y = (uint)(ymult * (float)win.anchor_vertical);
    13481686    m_xAnchorPoint = win.anchor_point % 3;
    13491687    m_yAnchorPoint = win.anchor_point / 3;
    1350     m_xAnchor = anchor_x;
     1688
     1689    // Should work equally for both left & right anchor points.
     1690    m_xAnchor = (m_xAnchorPoint == 1) ? anchor_x : xoffset + anchor_x;
    13511691    m_yAnchor = anchor_y;
    13521692
     1693    LOG(VB_VBI, LOG_INFO,
     1694        QString("708 centerWidth=%1 xoffset=%2 anchorPoints=%3,%4 anchors=%5,%6")
     1695        .arg(centerWidth).arg(xoffset)
     1696        .arg(m_xAnchorPoint).arg(m_yAnchorPoint)
     1697        .arg(m_xAnchor).arg(m_yAnchor));
     1698
    13531699    for (uint i = 0; i < list.size(); i++)
    13541700    {
    13551701        if (list[i]->y >= (uint)m_lines.size())
    SubtitleScreen::SubtitleScreen(MythPlayer *player, const char * name, 
    13691715    m_player(player),  m_subreader(NULL),   m_608reader(NULL),
    13701716    m_708reader(NULL), m_safeArea(QRect()),
    13711717    m_removeHTML(QRegExp("</?.+>")),        m_subtitleType(kDisplayNone),
    1372     m_fontSize(0),
    13731718    m_textFontZoom(100), m_textFontZoomPrev(100),
    13741719    m_textFontDelayMs(0), m_textFontDelayMsPrev(0),
    13751720    m_refreshModified(false), m_refreshDeleted(false),
    13761721    m_fontStretch(fontStretch),
    1377     m_format(new SubtitleFormat)
     1722    m_format(new SubtitleFormat(this, player, fontStretch))
    13781723{
    13791724    m_removeHTML.setMinimal(true);
    13801725
    void SubtitleScreen::Clear708Cache(uint64_t mask) 
    15881933// the subtitle screen.
    15891934void SubtitleScreen::SetElementAdded(void)
    15901935{
     1936    if (!m_refreshModified)
     1937        SetRedraw();
    15911938    m_refreshModified = true;
    15921939}
    15931940
    static QSize CalcShadowOffsetPadding(MythFontProperties *mythfont) 
    16201967    if (mythfont->hasOutline())
    16211968    {
    16221969        mythfont->GetOutline(color, outlineSize, alpha);
    1623         MythPoint outline(outlineSize, outlineSize);
    1624         outline.NormPoint();
    1625         outlineSize = outline.x();
     1970        // Outline has already been scaled by SubtitleFormat::Load
     1971        // MythPoint outline(outlineSize, outlineSize);
     1972        // outline.NormPoint();
     1973        // outlineSize = outline.x();
    16261974    }
    16271975    if (mythfont->hasShadow())
    16281976    {
    16291977        MythPoint shadowOffset;
    16301978        mythfont->GetShadow(shadowOffset, color, alpha);
    1631         shadowOffset.NormPoint();
     1979        // Shadow offset has already been scaled from theme to screen
     1980        // Why do it again?
     1981        // shadowOffset.NormPoint();
    16321982        shadowWidth = abs(shadowOffset.x());
    16331983        shadowHeight = abs(shadowOffset.y());
    16341984        // Shadow and outline overlap, so don't just add them.
    static QSize CalcShadowOffsetPadding(MythFontProperties *mythfont) 
    16401990
    16411991QSize SubtitleScreen::CalcTextSize(const QString &text,
    16421992                                   const CC708CharacterAttribute &format,
    1643                                    float layoutSpacing) const
     1993                                   float layoutSpacing,
     1994                                   MythFontProperties* mythfont) const
    16441995{
    1645     MythFontProperties *mythfont = GetFont(format);
     1996    if (!mythfont)
     1997        mythfont = GetFont(format);
    16461998    QFont *font = mythfont->GetFace();
    16471999    QFontMetrics fm(*font);
    16482000    int width = fm.width(text);
    1649     int height = fm.height() * (1 + PAD_HEIGHT);
     2001
     2002    // Font metrics for various fonts are inconsistent in how
     2003    // they provide a realistic line height value.
     2004    // The max of what they call height and lineSpacing
     2005    // provides a resonable value for most fonts.
     2006    int height = max(fm.height(), fm.lineSpacing());
     2007
    16502008    if (layoutSpacing > 0 && !text.trimmed().isEmpty())
    16512009        height = max(height, (int)(font->pixelSize() * layoutSpacing));
    16522010    height += CalcShadowOffsetPadding(mythfont).height();
    QSize SubtitleScreen::CalcTextSize(const QString &text, 
    16582016// right needs to add the shadow and offset padding.
    16592017void SubtitleScreen::CalcPadding(const CC708CharacterAttribute &format,
    16602018                                 bool isFirst, bool isLast,
    1661                                  int &left, int &right) const
     2019                                 int &left, int &right,
     2020                                 MythFontProperties* mythfont) const
    16622021{
    1663     MythFontProperties *mythfont = GetFont(format);
     2022    if (!mythfont)
     2023        mythfont = GetFont(format);
    16642024    QFont *font = mythfont->GetFace();
    16652025    QFontMetrics fm(*font);
    16662026    int basicPadding = fm.maxWidth() * PAD_WIDTH;
    void SubtitleScreen::CalcPadding(const CC708CharacterAttribute &format, 
    16722032MythFontProperties *
    16732033SubtitleScreen::GetFont(const CC708CharacterAttribute &attr) const
    16742034{
    1675     return m_format->GetFont(m_family, attr, m_fontSize,
    1676                              m_textFontZoom, m_fontStretch);
     2035    return m_format->GetFont(m_family, attr, m_textFontZoom);
     2036}
     2037
     2038QRect
     2039SubtitleScreen::GetSafeArea()
     2040{
     2041    return m_player->GetVideoOutput()->GetSafeRect();
    16772042}
    16782043
    16792044QString SubtitleScreen::GetTeletextFontName(void)
    16802045{
    16812046    SubtitleFormat format;
    1682     CC708CharacterAttribute attr(false, false, false, Qt::white);
    1683     MythFontProperties *mythfont =
    1684         format.GetFont(kSubFamilyTeletext, attr,
    1685                        /*pixelsize*/20, /*zoom*/100, /*stretch*/100);
    1686     return mythfont->face().family();
     2047    return format.GetTeletextFontName();
    16872048}
    16882049
    16892050int SubtitleScreen::GetTeletextBackgroundAlpha(void)
    void SubtitleScreen::OptimiseDisplayedArea(void) 
    18042165        return;
    18052166
    18062167    QRect bounding  = visible.boundingRect();
     2168    LOG(VB_VBI, LOG_DEBUG,
     2169        QString("Visible bounds=%1x%2@%3,%4")
     2170        .arg(bounding.width()).arg(bounding.height())
     2171        .arg(bounding.x()).arg(bounding.y()));
     2172
    18072173    bounding = bounding.translated(m_safeArea.topLeft());
     2174    LOG(VB_VBI, LOG_DEBUG,
     2175        QString("Translated bounds=%1x%2@%3,%4")
     2176        .arg(bounding.width()).arg(bounding.height())
     2177        .arg(bounding.x()).arg(bounding.y()));
     2178
    18082179    bounding = m_safeArea.intersected(bounding);
    18092180    int left = m_safeArea.left() - bounding.left();
    18102181    int top  = m_safeArea.top()  - bounding.top();
     2182    LOG(VB_VBI, LOG_DEBUG,
     2183        QString("Intersected bounds=%1x%2@%3,%4 left=%5 top=%6")
     2184        .arg(bounding.width()).arg(bounding.height())
     2185        .arg(bounding.x()).arg(bounding.y())
     2186        .arg(left).arg(top));
    18112187    SetArea(MythRect(bounding));
    18122188
    18132189    i.toFront();
  • mythtv/libs/libmythtv/subtitlescreen.h

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.h b/mythtv/libs/libmythtv/subtitlescreen.h
    index 1c7f74a..bb27768 100644
    a b public: 
    3535        : text(t), m_format(formatting), parent(p), textFont(NULL) {}
    3636    FormattedTextChunk(void) : parent(NULL), textFont(NULL) {}
    3737
    38     QSize CalcSize(float layoutSpacing = 0.0f) const;
    39     void CalcPadding(bool isFirst, bool isLast, int &left, int &right) const;
     38    QSize CalcSize(float layoutSpacing = 0.0f);
     39    void CalcPadding(bool isFirst, bool isLast, int &left, int &right);
    4040    bool Split(FormattedTextChunk &newChunk);
    4141    QString ToLogString(void) const;
    4242    bool PreRender(bool isFirst, bool isLast, int &x, int y, int height);
    public: 
    5959    FormattedTextLine(int x = -1, int y = -1, int o_x = -1, int o_y = -1)
    6060        : x_indent(x), y_indent(y), orig_x(o_x), orig_y(o_y) {}
    6161
    62     QSize CalcSize(float layoutSpacing = 0.0f) const;
     62    QSize CalcSize(float layoutSpacing = 0.0f, bool addPadding = true);
    6363
    6464    QList<FormattedTextChunk> chunks;
    6565    int x_indent; // -1 means TBD i.e. centered
    public: 
    190190
    191191    QSize CalcTextSize(const QString &text,
    192192                       const CC708CharacterAttribute &format,
    193                        float layoutSpacing) const;
     193                       float layoutSpacing,
     194                       MythFontProperties* mythfont = 0) const;
    194195    void CalcPadding(const CC708CharacterAttribute &format,
    195196                     bool isFirst, bool isLast,
    196                      int &left, int &right) const;
     197                     int &left, int &right,
     198                     MythFontProperties* mythfont = 0) const;
    197199    MythFontProperties* GetFont(const CC708CharacterAttribute &attr) const;
    198     void SetFontSize(int pixelSize) { m_fontSize = pixelSize; }
     200    QRect GetSafeArea();
    199201
    200202    // Temporary methods until teletextscreen.cpp is refactored into
    201203    // subtitlescreen.cpp
    private: 
    230232    QRect              m_safeArea;
    231233    QRegExp            m_removeHTML;
    232234    int                m_subtitleType;
    233     int                m_fontSize;
    234235    int                m_textFontZoom; // valid for 708 & text subs
    235236    int                m_textFontZoomPrev;
    236237    int                m_textFontDelayMs; // valid for text subs