Ticket #12057: 12057.patch

File 12057.patch, 37.6 KB (added by faginbagin <mythtv@…>, 10 years ago)
  • mythtv/libs/libmythtv/subtitlescreen.cpp

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.cpp b/mythtv/libs/libmythtv/subtitlescreen.cpp
    index 965e3b0..b40b9cf 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: 
    143211                            int start, int duration);
    144212    static QString MakePrefix(const QString &family,
    145213                              const CC708CharacterAttribute &attr);
     214
     215    // Temporary method until teletextscreen.cpp is refactored into
     216    // subtitlescreen.cpp
     217    // GetTeletextFontName is the only public method that should be called
     218    // when m_subScreen and m_player are null
     219    QString GetTeletextFontName(void);
     220
    146221private:
    147222    void Load(const QString &family,
    148223              const CC708CharacterAttribute &attr);
    private: 
    164239                              MythUIShape *bg1,
    165240                              MythUIShape *bg2);
    166241
     242    // Hash table of default font properties by family
    167243    QHash<QString, MythFontProperties *> m_fontMap;
    168244    QHash<QString, MythUIShape *>       m_shapeMap;
    169245    QHash<QString, QSet<QString> >     m_changeMap;
    private: 
    173249    QHash<QString, int>             m_pixelSizeMap;
    174250    QHash<QString, int>           m_outlineSizeMap;
    175251    QHash<QString, QPoint>       m_shadowOffsetMap;
     252
     253    // Hash table of font properties by family, atttributes, zoom & stretch
     254    QHash<SubtitleFontKey, MythFontProperties *> m_fullFontMap;
     255
    176256    QVector<MythUIType *> m_cleanup;
     257    SubtitleScreen* m_subScreen;
     258    MythPlayer* m_player;
     259    int m_fontStretch;
    177260};
    178261
    179262static const QString kSubProvider("provider");
    static QString fontToString(MythFontProperties *f) 
    243326MythFontProperties *
    244327SubtitleFormat::GetFont(const QString &family,
    245328                        const CC708CharacterAttribute &attr,
    246                         int pixelSize, int zoom, int stretch)
     329                        int zoom)
    247330{
    248     int origPixelSize = pixelSize;
     331    QString prefix = MakePrefix(family, attr);
     332    SubtitleFontKey key(prefix, attr, zoom, m_fontStretch);
     333    if (m_fullFontMap.contains(key))
     334        return m_fullFontMap[key];
     335
     336    if (!m_fontMap.contains(prefix))
     337        Load(family, attr);
     338
     339    // Make a copy we can modify once and reuse many times
     340    MythFontProperties *result = new MythFontProperties(*(m_fontMap[prefix]));
     341    m_fullFontMap[key] = result;
     342
    249343    float scale = zoom / 100.0;
    250344    if ((attr.pen_size & 0x3) == k708AttrSizeSmall)
    251345        scale = scale * 32 / 42;
    252346    else if ((attr.pen_size & 0x3) == k708AttrSizeLarge)
    253347        scale = scale * 42 / 32;
    254348
    255     QString prefix = MakePrefix(family, attr);
    256     if (!m_fontMap.contains(prefix))
    257         Load(family, attr);
    258     MythFontProperties *result = m_fontMap[prefix];
    259 
    260349    // Apply the scaling factor to pixelSize even if the theme
    261350    // explicitly sets pixelSize.
    262     if (!IsUnlocked(prefix, kSubAttrPixelsize))
    263         pixelSize = m_pixelSizeMap[prefix];
     351    int pixelSize = m_pixelSizeMap[prefix];
     352    int origPixelSize = pixelSize;
    264353    pixelSize *= scale;
    265354    result->GetFace()->setPixelSize(pixelSize);
    266355
    267     result->GetFace()->setStretch(stretch);
     356    // Stretch is now set in SubtitleFormat::Load
     357    // result->GetFace()->setStretch(stretch);
     358
    268359    if (IsUnlocked(prefix, kSubAttrItalics))
    269360        result->GetFace()->setItalic(attr.italics);
    270361    if (IsUnlocked(prefix, kSubAttrUnderline))
    SubtitleFormat::GetFont(const QString &family, 
    300391    else
    301392    {
    302393        offset = m_shadowOffsetMap[prefix];
    303         offset.NormPoint();
     394        // Shadow offset has already been scaled from theme to screen
     395        // Why do it again?
     396        // offset.NormPoint();
    304397        offset.setX(offset.x() * scale + 0.5);
    305398        offset.setY(offset.y() * scale + 0.5);
    306399    }
    SubtitleFormat::GetFont(const QString &family, 
    328421    else
    329422    {
    330423        off = m_outlineSizeMap[prefix];
    331         MythPoint point(off, off);
    332         point.NormPoint();
    333         off = point.x() * scale + 0.5;
     424        // Outline has already been scaled by SubtitleFormat::Load.
     425        // Why do it again?
     426        // MythPoint point(off, off);
     427        // point.NormPoint();
     428        off = off * scale + 0.5;
    334429    }
    335430    result->SetOutline(outline, color, off, alpha);
    336431
    337432    LOG(VB_VBI, LOG_DEBUG,
    338433        QString("GetFont(family=%1, prefix=%2, orig pixelSize=%3, "
    339                 "new pixelSize=%4 zoom=%5) = %6")
    340         .arg(family).arg(prefix).arg(origPixelSize).arg(pixelSize)
    341         .arg(zoom).arg(fontToString(result)));
     434                "zoom=%4, stretch=%5) = %6")
     435        .arg(family).arg(prefix).arg(origPixelSize)
     436        .arg(zoom).arg(m_fontStretch).arg(fontToString(result)));
    342437    return result;
    343438}
    344439
     440QString SubtitleFormat::GetTeletextFontName(void)
     441{
     442    CC708CharacterAttribute attr(false, false, false, Qt::white);
     443
     444    QString prefix = MakePrefix(kSubFamilyTeletext, attr);
     445    if (!m_fontMap.contains(prefix))
     446        Load(kSubFamilyTeletext, attr);
     447    MythFontProperties *result = m_fontMap[prefix];
     448    return result->face().family();
     449}
     450
     451SubtitleFormat::SubtitleFormat(SubtitleScreen* screen, MythPlayer* player,
     452                               int stretch) :
     453    m_subScreen(screen), m_player(player), m_fontStretch(stretch)
     454{
     455}
     456
    345457SubtitleFormat::~SubtitleFormat(void)
    346458{
     459    QHash<SubtitleFontKey, MythFontProperties*>::const_iterator it;
     460    for (it = m_fullFontMap.constBegin(); it !=  m_fullFontMap.constEnd(); it++)
     461    {
     462        delete (*it);
     463    }
     464
    347465    for (int i = 0; i < m_cleanup.size(); ++i)
    348466    {
    349467        m_cleanup[i]->DeleteAllChildren();
    void SubtitleFormat::CreateProviderDefault(const QString &family, 
    402520
    403521    if (isComplement)
    404522        Complement(font, bg);
    405     parent->AddFont(kSubProvider, font);
    406523
    407     *returnFont = font;
     524    // This ultimately calls FontMap::AddFont, which makes a deep copy
     525    // so we need to delete font.
     526    // In case something changes underneath, we make sure the value
     527    // returned by parent->GetFont is indeed different.
     528    parent->AddFont(kSubProvider, font);
     529    *returnFont = parent->GetFont(kSubProvider);
     530    if (*returnFont != font)
     531        delete font;
    408532    *returnBg = bg;
    409533}
    410534
    void SubtitleFormat::Load(const QString &family, 
    501625    QPoint offset;
    502626    QColor color;
    503627    int alpha;
    504     int size;
    505628    resultFont->GetShadow(offset, color, alpha);
     629    m_shadowOffsetMap[prefix] = offset;
     630
     631    // Shadow offset is scaled from theme to screen when it is parsed.
     632    // But outline size is not.
     633    // As long as this inconsistency exists, we scale outline size here.
     634    int size;
    506635    resultFont->GetOutline(color, size, alpha);
     636    MythPoint outline(size, size);
     637    outline.NormPoint();
     638    size = outline.x();
    507639    m_outlineSizeMap[prefix] = size;
    508     m_shadowOffsetMap[prefix] = offset;
    509     m_pixelSizeMap[prefix] = resultFont->GetFace()->pixelSize();
    510640
    511     delete negFont;
     641    // Fixed memory leak in SubtitleFormat::CreateProviderDefault,
     642    // so negFont shouldn't be deleted here.
     643    // It will be deleted later when negParent is deleted via m_cleanup
     644    // delete negFont;
     645
     646    // Font stretch is constant for the life of each SubtitleScreen instance.
     647    // So the same is true for SubtitleFormat.
     648    // It is 100 for most playback profiles.
     649    // But the Slim profile changes it whenever the video zoom factor changes.
     650    // When that happens, the current SubtitleScreen is deleted
     651    // and a new SubtitleScreen with a new font stretch is instantiated.
     652    resultFont->GetFace()->setStretch(m_fontStretch);
     653
     654    // If the user hasn't defined a pixelsize
     655    // and not called via teletextscreen.cpp (where m_subScreen is null),
     656    // set a default
     657    if (IsUnlocked(prefix, kSubAttrPixelsize) && m_subScreen != 0)
     658    {
     659        QRect safeArea = m_subScreen->GetSafeArea();
     660        LOG(VB_VBI, LOG_DEBUG,
     661            QString("safeArea=%1x%2@%3,%4")
     662            .arg(safeArea.width())
     663            .arg(safeArea.height())
     664            .arg(safeArea.x())
     665            .arg(safeArea.y()));
     666
     667        // Start with 608 row,col dimensions
     668        int cols = 32;    // 608 max columns
     669        int rows = 15;    // 608 max rows
     670
     671        // Adjust by family
     672        if (kSubFamily708 == family || kSubFamilyText == family)
     673        {
     674            // Assumption: .srt and .txt line lengths will be within
     675            // the limits of 708 captions, whose # of columns depends on
     676            // the source video's aspect ratio.
     677            cols = (m_player->GetVideoAspect() > 1.4f) ? 42 : 32;
     678            rows = 15;
     679        }
     680        //else if (kSubFamilyAV == family)
     681        //{
     682        //    DVB bitmapped subtitles?
     683        //}
     684        else if (kSubFamilyTeletext == family)
     685        {
     686            // Constants defined in teletextscreen.cpp
     687            // Copied values rather than depend on something that
     688            // going to be refactored?
     689            cols = 40; // TeletextScreen::kTeletextColumns
     690            rows = 26; // TeletextScreen::kTeletextRows
     691        }
     692
     693        // Add one to cols to account for 1/2 char width left&right padding.
     694        cols++;
     695
     696        // Target lineHeight
     697        int lineHeight = safeArea.height() / rows;
     698
     699        // Set pixelSize to the target lineHeight
     700        int pixelSize = lineHeight;
     701        // Reduce by shadow offset or outline size
     702        pixelSize -= max(abs(offset.y()), size);
     703        // It is a first guess pixelSize, which will almost certainly be off
     704
     705        QFont *font = resultFont->GetFace();
     706        font->setPixelSize(pixelSize);
     707        QFontMetrics fm(*font);
     708
     709        // Font metrics for various fonts are inconsistent in how
     710        // they provide a realistic line height value.
     711        // The max of what they call height and lineSpacing
     712        // provides a resonable value for most fonts.
     713        int newLineHeight = max(fm.height(), fm.lineSpacing());
     714        // Increase by shadow offset or outline size
     715        newLineHeight += max(abs(offset.y()), size);
     716
     717        int offBy = newLineHeight - lineHeight;
     718        int newPixelSize = pixelSize;
     719        if (offBy != 0)
     720        {
     721            // We tried a pixelSize equal to the desired line height
     722            // with shadow & outline adjustments,
     723            // but it didn't quite get us there.
     724            // We make a better guess by applying a ratio of
     725            // our first guess divided by the actual height.
     726            // This should be "good enough" for most fonts.
     727            newPixelSize = (int)((float)pixelSize*pixelSize/newLineHeight + 0.5);
     728            font->setPixelSize(newPixelSize);
     729            fm = QFontMetrics(*font);
     730            newLineHeight = max(fm.height(), fm.lineSpacing());
     731            newLineHeight += max(abs(offset.y()), size);
     732        }
     733
     734        int fontwidth = fm.averageCharWidth();
     735        int maxwidth = (safeArea.width() - max(abs(offset.x()), size)) / cols;
     736
     737        LOG(VB_VBI, LOG_DEBUG,
     738            QString("family=%1 default-pixelSize=%2->%3 lineHeight=%4->%5 "
     739                    "offBy=%6 fontwidth=%7 maxwidth=%8 shadow-offset=%9,%10 "
     740                    "outline=%11")
     741            .arg(family).arg(pixelSize).arg(newPixelSize)
     742            .arg(lineHeight).arg(newLineHeight)
     743            .arg(offBy).arg(fontwidth).arg(maxwidth)
     744            .arg(offset.x()).arg(offset.y()).arg(size));
     745
     746        // Check if fontwidth exceeds maxwidth
     747        // Just log it for now. We'll deal with it, if/when someone
     748        // reports a problem.
     749        if (fontwidth > maxwidth)
     750        {
     751            LOG(VB_GENERAL, LOG_WARNING,
     752                QString("fontwidth=%1 exceeds maxwidth=%2")
     753                .arg(fontwidth).arg(maxwidth));
     754        }
     755
     756        m_pixelSizeMap[prefix] = newPixelSize;
     757    }
     758    else
     759    {
     760        int pixelSize = resultFont->GetFace()->pixelSize();
     761        LOG(VB_VBI, LOG_DEBUG,
     762            QString("family=%1 user defined pixelSize=%2")
     763            .arg(family).arg(pixelSize));
     764        m_pixelSizeMap[prefix] = pixelSize;
     765    }
    512766}
    513767
    514768QSet<QString> SubtitleFormat::Diff(const QString &family,
    SubtitleFormat::GetBackground(MythUIType *parent, const QString &name, 
    615869
    616870////////////////////////////////////////////////////////////////////////////
    617871
    618 QSize FormattedTextChunk::CalcSize(float layoutSpacing) const
     872QSize FormattedTextChunk::CalcSize(float layoutSpacing)
    619873{
    620     return parent->CalcTextSize(text, m_format, layoutSpacing);
     874    if (!textFont)
     875        textFont = parent->GetFont(m_format);
     876    return parent->CalcTextSize(text, m_format, layoutSpacing, textFont);
    621877}
    622878
    623879void FormattedTextChunk::CalcPadding(bool isFirst, bool isLast,
    624                                      int &left, int &right) const
     880                                     int &left, int &right)
    625881{
    626     parent->CalcPadding(m_format, isFirst, isLast, left, right);
     882    if (!textFont)
     883        textFont = parent->GetFont(m_format);
     884    parent->CalcPadding(m_format, isFirst, isLast, left, right, textFont);
    627885}
    628886
    629887bool FormattedTextChunk::Split(FormattedTextChunk &newChunk)
    QString FormattedTextChunk::ToLogString(void) const 
    673931bool FormattedTextChunk::PreRender(bool isFirst, bool isLast,
    674932                                   int &x, int y, int height)
    675933{
    676     textFont = parent->GetFont(m_format);
    677934    if (!textFont)
    678         return false;
     935        textFont = parent->GetFont(m_format);
     936
    679937    QFontMetrics font(*(textFont->GetFace()));
    680938    // If the chunk starts with whitespace, the leading whitespace
    681939    // ultimately gets lost due to the text.trimmed() operation in the
    bool FormattedTextChunk::PreRender(bool isFirst, bool isLast, 
    690948    int leftPadding, rightPadding;
    691949    CalcPadding(isFirst, isLast, leftPadding, rightPadding);
    692950    // Account for extra padding before the first chunk.
    693     if (isFirst)
    694         x += leftPadding;
     951    // No, this will cause everything to be off center to the right
     952    // by the value of leftPadding.
     953    // if (isFirst)
     954    //     x += leftPadding;
    695955    QSize chunk_sz = CalcSize();
    696956    QRect bgrect(x - leftPadding, y,
    697957                 chunk_sz.width() + leftPadding + rightPadding,
    bool FormattedTextChunk::PreRender(bool isFirst, bool isLast, 
    710970    textRect = QRect(x + x_adjust, y,
    711971                     chunk_sz.width() - x_adjust + rightPadding, height);
    712972
     973    // If this chunk is in italics, add a few pixels to the width.
     974    // This is to compensate for some font/char combos that would otherwise
     975    // have the top right corner of the last char clipped.
     976    // The effect can be seen with Droid Sans Mono and certain upper case chars
     977    // like E or W.
     978    // But don't add this padding to the next chunk's x coordinate.
     979    // That would add too much spacing between chunks.
     980    if (m_format.italics)
     981    {
     982        int oldWidth = textRect.width();
     983        textRect.setWidth(oldWidth + font.averageCharWidth() / 2);
     984        LOG(VB_VBI, LOG_DEBUG,
     985            QString("Pad for italics width %1 -> %2")
     986            .arg(oldWidth).arg(textRect.width()));
     987    }
     988
    713989    x += chunk_sz.width();
    714990    return true;
    715991}
    716992
    717 QSize FormattedTextLine::CalcSize(float layoutSpacing) const
     993QSize FormattedTextLine::CalcSize(float layoutSpacing, bool addPadding)
    718994{
    719995    int height = 0, width = 0;
    720996    int leftPadding = 0, rightPadding = 0;
    721     QList<FormattedTextChunk>::const_iterator it;
    722     bool isFirst = true;
    723     for (it = chunks.constBegin(); it != chunks.constEnd(); ++it)
     997    QList<FormattedTextChunk>::iterator it;
     998    bool isFirst = (addPadding ? true : false);
     999    for (it = chunks.begin(); it != chunks.end(); ++it)
    7241000    {
    725         bool isLast = (it + 1 == chunks.constEnd());
     1001        bool isLast = (addPadding ? (it + 1 == chunks.constEnd()) : false);
    7261002        QSize tmp = (*it).CalcSize(layoutSpacing);
    7271003        height = max(height, tmp.height());
    7281004        width += tmp.width();
    729         (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
     1005        // We don't need to call CalcPadding if addPadding is false
     1006        if (addPadding)
     1007            (*it).CalcPadding(isFirst, isLast, leftPadding, rightPadding);
    7301008        if (it == chunks.constBegin())
    7311009            width += leftPadding;
    7321010        isFirst = false;
    FormattedTextSubtitle::FormattedTextSubtitle(void) : 
    7611039// Normal font height is designed to be 1/20 of safe area height, with
    7621040// extra blank space between lines to make 17 lines within the safe
    7631041// area.
    764 static const float LINE_SPACING = (20.0 / 17.0);
     1042// static const float LINE_SPACING = (20.0 / 17.0);
     1043static const float LINE_SPACING = 0.0;
    7651044static const float PAD_WIDTH  = 0.5;
    766 static const float PAD_HEIGHT = 0.04;
     1045// static const float PAD_HEIGHT = 0.04;
    7671046
    7681047// Resolves any TBD x_indent and y_indent values in FormattedTextLine
    7691048// objects.  Calculates m_bounds.  Prunes most leading and all
    void FormattedTextSubtitle::Layout(void) 
    7761055    int anchor_height = 0;
    7771056    for (int i = 0; i < m_lines.size(); i++)
    7781057    {
    779         QSize sz = m_lines[i].CalcSize(LINE_SPACING);
     1058        // We don't want padding added at this point.
     1059        // Padding applies to the background.
     1060        // We want to know where the txt should be drawn,
     1061        // not where the background shoould be drawn.
     1062        QSize sz = m_lines[i].CalcSize(LINE_SPACING, false);
    7801063        anchor_width = max(anchor_width, sz.width());
    7811064        anchor_height += sz.height();
    7821065    }
    void FormattedTextSubtitle::Layout(void) 
    7971080    anchor_y = max(0, min(anchor_y, m_safeArea.height() - anchor_height));
    7981081    anchor_x = max(0, min(anchor_x, m_safeArea.width() - anchor_width));
    7991082
    800     m_bounds = QRect(anchor_x, anchor_y, anchor_width, anchor_height);
     1083    // m_bounds needs padding.
     1084    // Get it using the first chunk of the first line.
     1085    int left = 0;
     1086    int right = 0;
     1087    m_lines[0].chunks.first().CalcPadding(true, true, left, right);
     1088    m_bounds = QRect(anchor_x-left,
     1089                     anchor_y,
     1090                     anchor_width+left+right,
     1091                     anchor_height);
    8011092
    8021093    // Fill in missing coordinates
    8031094    int y = anchor_y;
    void FormattedTextSubtitle::Layout(void) 
    8071098            m_lines[i].x_indent = anchor_x;
    8081099        if (m_lines[i].y_indent < 0)
    8091100            m_lines[i].y_indent = y;
    810         y += m_lines[i].CalcSize(LINE_SPACING).height();
     1101        y += m_lines[i].CalcSize(LINE_SPACING, false).height();
    8111102        // Prune leading all-whitespace chunks.
    8121103        while (!m_lines[i].chunks.isEmpty() &&
    8131104               m_lines[i].chunks.first().text.trimmed().isEmpty())
    void FormattedTextSubtitle::PreRender(void) 
    8421133    {
    8431134        int x = m_lines[i].x_indent;
    8441135        int y = m_lines[i].y_indent;
    845         int height = m_lines[i].CalcSize().height();
     1136        int height = m_lines[i].CalcSize(LINE_SPACING, false).height();
    8461137        QList<FormattedTextChunk>::iterator chunk;
    8471138        bool isFirst = true;
    8481139        for (chunk = m_lines[i].chunks.begin();
    void FormattedTextSubtitle::Draw(void) 
    8711162        {
    8721163            MythFontProperties *mythfont =
    8731164                m_subScreen->GetFont((*chunk).m_format);
    874             if (!mythfont)
    875                 continue;
     1165            // Never returns NULL
     1166            // if (!mythfont)
     1167            //     continue;
    8761168            // Note: NULL is passed as the parent argument to the
    8771169            // MythUI constructors so that we can control the drawing
    8781170            // order of the children.  In particular, background
    8791171            // shapes should be added/drawn first, and text drawn on
    8801172            // top.
     1173            LOG(VB_VBI, LOG_DEBUG,
     1174                QString("i=%1 rect=%2x%3@%4,%5 bgrect=%6x%7@%8,%9 text=%10")
     1175                .arg(i)
     1176                .arg(chunk->textRect.width())
     1177                .arg(chunk->textRect.height())
     1178                .arg(chunk->textRect.x())
     1179                .arg(chunk->textRect.y())
     1180                .arg(chunk->bgShapeRect.width())
     1181                .arg(chunk->bgShapeRect.height())
     1182                .arg(chunk->bgShapeRect.x())
     1183                .arg(chunk->bgShapeRect.y())
     1184                .arg(chunk->text));
    8811185            if ((*chunk).textRect.width() > 0) {
    8821186                SubSimpleText *text =
    8831187                    new SubSimpleText((*chunk).text, *mythfont,
    void FormattedTextSubtitleSRT::Init(const QStringList &subs) 
    9721276    // <font color="#xxyyzz"> - change font color
    9731277    // </font> - reset font color to white
    9741278
    975     int pixelSize = m_safeArea.height() / 20;
    976     if (m_subScreen)
    977         m_subScreen->SetFontSize(pixelSize);
    9781279    m_xAnchorPoint = 1; // center
    9791280    m_yAnchorPoint = 2; // bottom
    9801281    m_xAnchor = m_safeArea.width() / 2;
    void FormattedTextSubtitle608::Layout(void) 
    11821483        int y = m_lines[i].y_indent;
    11831484        if (i == 0)
    11841485            firstY = prevY = y;
    1185         int height = m_lines[i].CalcSize().height();
     1486        int height = m_lines[i].CalcSize(LINE_SPACING, false).height();
    11861487        heights[i] = height;
    11871488        spaceBefore[i] = y - prevY;
    11881489        totalSpace += (y - prevY);
    void FormattedTextSubtitle608::Layout(void) 
    12031504            m_lines[i].y_indent = prevY + spaceBefore[i] * shrink;
    12041505            prevY = m_lines[i].y_indent + heights[i];
    12051506        }
     1507        LOG(VB_VBI, LOG_DEBUG,
     1508            QString("Shrink overage=%1 totalSpace=%2 shrink=%3 firstY=%4 prevY=%5")
     1509            .arg(overage).arg(totalSpace).arg(shrink).arg(firstY).arg(prevY));
    12061510    }
    12071511
    12081512    // Shift Y coordinates back up into the safe area.
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12241528    if (buffers.empty())
    12251529        return;
    12261530    vector<CC608Text*>::const_iterator i = buffers.begin();
    1227     int xscale = 36;
    1228     int yscale = 17;
    1229     int pixelSize = m_safeArea.height() / (yscale * LINE_SPACING);
     1531
     1532    int xscale = 32;    // 608 max columns
     1533    int yscale = 15;    // 608 max rows
     1534
     1535    int lineHeight = m_safeArea.height() / yscale;
    12301536    int fontwidth = 0;
    12311537    int xmid = 0;
    1232     int zoom = 100;
     1538    // int zoom = 100;
    12331539    if (m_subScreen)
    12341540    {
    1235         zoom = m_subScreen->GetZoom();
    1236         m_subScreen->SetFontSize(pixelSize);
     1541        // zoom = m_subScreen->GetZoom();
    12371542        CC708CharacterAttribute def_attr(false, false, false, clr[0]);
    1238         QFont *font = m_subScreen->GetFont(def_attr)->GetFace();
     1543        MythFontProperties* mythfont = m_subScreen->GetFont(def_attr);
     1544        QFont *font = mythfont->GetFace();
    12391545        QFontMetrics fm(*font);
    12401546        fontwidth = fm.averageCharWidth();
    12411547        xmid = m_safeArea.width() / 2;
     1548
     1549        // Font metrics for various fonts are inconsistent in how
     1550        // they provide a realistic line height value.
     1551        // The max of what they call height and lineSpacing
     1552        // provides a resonable value for most fonts.
     1553        lineHeight = max(fm.height(), fm.lineSpacing());
     1554
     1555        // Increase by shadow offset and/or outline size
     1556        QPoint offset;
     1557        QColor color;
     1558        int alpha;
     1559        mythfont->GetShadow(offset, color, alpha);
     1560
     1561        int size;
     1562        mythfont->GetOutline(color, size, alpha);
     1563
     1564        lineHeight += max(abs(offset.y()), size);
     1565
    12421566        // Disable centering for zoom factor >= 100%
    1243         if (zoom >= 100)
    1244             xscale = m_safeArea.width() / fontwidth;
     1567        // Why?? If disabled, 608 captions are shifted to left unless
     1568        // the user sets the zoom to 99% or less.
     1569        // The Layout method should shift left-right and up-down
     1570        // as needed for zoom factor.
     1571        // If zoom factor is too large, the user can reduce it.
     1572        // If they don't we'll clip what doesn't fit.
     1573        //if (zoom >= 100)
     1574        //    xscale = m_safeArea.width() / fontwidth;
    12451575    }
    12461576
    12471577    for (; i != buffers.end(); ++i)
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12521582        const bool isBold = false;
    12531583        QString text(cc->text);
    12541584
     1585        // Note: cc-x (and orig_x) is column # ranging from 0 through 31,
    12551586        int orig_x = cc->x;
    12561587        // position as if we use a fixed size font
    12571588        // - font size already has zoom factor applied
    void FormattedTextSubtitle608::Init(const vector<CC608Text*> &buffers) 
    12641595            // fallback
    12651596            x = (orig_x + 3) * m_safeArea.width() / xscale;
    12661597
     1598        // Note: cc-y (and orig_y) is row # ranging from 1 through 15,
     1599        // we need zero based rows
    12671600        int orig_y = cc->y;
    12681601        int y;
    1269         if (orig_y < yscale / 2)
     1602        if (orig_y <= yscale / 2)
    12701603            // top half -- anchor up
    1271             y = (orig_y * m_safeArea.height() * zoom / (yscale * 100));
     1604            y = (orig_y-1) * lineHeight;
    12721605        else
    12731606            // bottom half -- anchor down
    1274             y = m_safeArea.height() -
    1275                 ((yscale - orig_y - 0.5) * m_safeArea.height() * zoom /
    1276                  (yscale * 100));
     1607            y = m_safeArea.height() - ((yscale - (orig_y-1)) * lineHeight);
    12771608
    12781609        FormattedTextLine line(x, y, orig_x, orig_y);
    12791610        while (!text.isNull())
    void FormattedTextSubtitle708::Init(const CC708Window &win, 
    13191650                "relative %5")
    13201651            .arg(m_num).arg(win.anchor_point).arg(win.anchor_horizontal)
    13211652            .arg(win.anchor_vertical).arg(win.relative_pos));
    1322     int pixelSize = m_safeArea.height() / 20;
    1323     if (m_subScreen)
    1324         m_subScreen->SetFontSize(pixelSize);
    13251653
    13261654    float xrange  = win.relative_pos ? 100.0f :
    13271655                    (aspect > 1.4f) ? 210.0f : 160.0f;
    13281656    float yrange  = win.relative_pos ? 100.0f : 75.0f;
    1329     float xmult   = (float)m_safeArea.width() / xrange;
     1657
     1658    // Center the window addressable area based on the first string's font.
     1659    // Assumption: Most caption providers do not change font within a window
     1660    MythFontProperties* mythFont = m_subScreen->GetFont(list[0]->attr);
     1661    QFont* font = mythFont->GetFace();
     1662    QFontMetrics fm(*font);
     1663    int cols = (aspect > 1.4f) ? 42 : 32;
     1664    int centerWidth = min(m_safeArea.width(), cols * fm.averageCharWidth());
     1665    int xoffset = (m_safeArea.width() - centerWidth) / 2;
     1666
     1667    float xmult   = (float)centerWidth / xrange;
    13301668    float ymult   = (float)m_safeArea.height() / yrange;
    13311669    uint anchor_x = (uint)(xmult * (float)win.anchor_horizontal);
    13321670    uint anchor_y = (uint)(ymult * (float)win.anchor_vertical);
    13331671    m_xAnchorPoint = win.anchor_point % 3;
    13341672    m_yAnchorPoint = win.anchor_point / 3;
    1335     m_xAnchor = anchor_x;
     1673
     1674    // Should work equally for both left & right anchor points.
     1675    m_xAnchor = (m_xAnchorPoint == 1) ? anchor_x : xoffset + anchor_x;
    13361676    m_yAnchor = anchor_y;
    13371677
     1678    LOG(VB_VBI, LOG_INFO,
     1679        QString("708 centerWidth=%1 xoffset=%2 anchorPoints=%3,%4 anchors=%5,%6")
     1680        .arg(centerWidth).arg(xoffset)
     1681        .arg(m_xAnchorPoint).arg(m_yAnchorPoint)
     1682        .arg(m_xAnchor).arg(m_yAnchor));
     1683
    13381684    for (uint i = 0; i < list.size(); i++)
    13391685    {
    13401686        if (list[i]->y >= (uint)m_lines.size())
    SubtitleScreen::SubtitleScreen(MythPlayer *player, const char * name, 
    13541700    m_player(player),  m_subreader(NULL),   m_608reader(NULL),
    13551701    m_708reader(NULL), m_safeArea(QRect()),
    13561702    m_removeHTML(QRegExp("</?.+>")),        m_subtitleType(kDisplayNone),
    1357     m_fontSize(0),
    13581703    m_textFontZoom(100), m_textFontZoomPrev(100),
    13591704    m_textFontDelayMs(0), m_textFontDelayMsPrev(0),
    13601705    m_refreshModified(false), m_refreshDeleted(false),
    13611706    m_fontStretch(fontStretch),
    1362     m_format(new SubtitleFormat)
     1707    m_format(new SubtitleFormat(this, player, fontStretch))
    13631708{
    13641709    m_removeHTML.setMinimal(true);
    13651710
    void SubtitleScreen::Clear708Cache(uint64_t mask) 
    15731918// the subtitle screen.
    15741919void SubtitleScreen::SetElementAdded(void)
    15751920{
     1921    if (!m_refreshModified)
     1922        SetRedraw();
    15761923    m_refreshModified = true;
    15771924}
    15781925
    static QSize CalcShadowOffsetPadding(MythFontProperties *mythfont) 
    16051952    if (mythfont->hasOutline())
    16061953    {
    16071954        mythfont->GetOutline(color, outlineSize, alpha);
    1608         MythPoint outline(outlineSize, outlineSize);
    1609         outline.NormPoint();
    1610         outlineSize = outline.x();
     1955        // Outline has already been scaled by SubtitleFormat::Load
     1956        // MythPoint outline(outlineSize, outlineSize);
     1957        // outline.NormPoint();
     1958        // outlineSize = outline.x();
    16111959    }
    16121960    if (mythfont->hasShadow())
    16131961    {
    16141962        MythPoint shadowOffset;
    16151963        mythfont->GetShadow(shadowOffset, color, alpha);
    1616         shadowOffset.NormPoint();
     1964        // Shadow offset has already been scaled from theme to screen
     1965        // Why do it again?
     1966        // shadowOffset.NormPoint();
    16171967        shadowWidth = abs(shadowOffset.x());
    16181968        shadowHeight = abs(shadowOffset.y());
    16191969        // Shadow and outline overlap, so don't just add them.
    static QSize CalcShadowOffsetPadding(MythFontProperties *mythfont) 
    16251975
    16261976QSize SubtitleScreen::CalcTextSize(const QString &text,
    16271977                                   const CC708CharacterAttribute &format,
    1628                                    float layoutSpacing) const
     1978                                   float layoutSpacing,
     1979                                   MythFontProperties* mythfont) const
    16291980{
    1630     MythFontProperties *mythfont = GetFont(format);
     1981    if (!mythfont)
     1982        mythfont = GetFont(format);
    16311983    QFont *font = mythfont->GetFace();
    16321984    QFontMetrics fm(*font);
    16331985    int width = fm.width(text);
    1634     int height = fm.height() * (1 + PAD_HEIGHT);
     1986
     1987    // Font metrics for various fonts are inconsistent in how
     1988    // they provide a realistic line height value.
     1989    // The max of what they call height and lineSpacing
     1990    // provides a resonable value for most fonts.
     1991    int height = max(fm.height(), fm.lineSpacing());
     1992
    16351993    if (layoutSpacing > 0 && !text.trimmed().isEmpty())
    16361994        height = max(height, (int)(font->pixelSize() * layoutSpacing));
    16371995    height += CalcShadowOffsetPadding(mythfont).height();
    QSize SubtitleScreen::CalcTextSize(const QString &text, 
    16432001// right needs to add the shadow and offset padding.
    16442002void SubtitleScreen::CalcPadding(const CC708CharacterAttribute &format,
    16452003                                 bool isFirst, bool isLast,
    1646                                  int &left, int &right) const
     2004                                 int &left, int &right,
     2005                                 MythFontProperties* mythfont) const
    16472006{
    1648     MythFontProperties *mythfont = GetFont(format);
     2007    if (!mythfont)
     2008        mythfont = GetFont(format);
    16492009    QFont *font = mythfont->GetFace();
    16502010    QFontMetrics fm(*font);
    16512011    int basicPadding = fm.maxWidth() * PAD_WIDTH;
    void SubtitleScreen::CalcPadding(const CC708CharacterAttribute &format, 
    16572017MythFontProperties *
    16582018SubtitleScreen::GetFont(const CC708CharacterAttribute &attr) const
    16592019{
    1660     return m_format->GetFont(m_family, attr, m_fontSize,
    1661                              m_textFontZoom, m_fontStretch);
     2020    return m_format->GetFont(m_family, attr, m_textFontZoom);
     2021}
     2022
     2023QRect
     2024SubtitleScreen::GetSafeArea()
     2025{
     2026    return m_player->GetVideoOutput()->GetSafeRect();
    16622027}
    16632028
    16642029QString SubtitleScreen::GetTeletextFontName(void)
    16652030{
    16662031    SubtitleFormat format;
    1667     CC708CharacterAttribute attr(false, false, false, Qt::white);
    1668     MythFontProperties *mythfont =
    1669         format.GetFont(kSubFamilyTeletext, attr,
    1670                        /*pixelsize*/20, /*zoom*/100, /*stretch*/100);
    1671     return mythfont->face().family();
     2032    return format.GetTeletextFontName();
    16722033}
    16732034
    16742035bool SubtitleScreen::Create(void)
    void SubtitleScreen::OptimiseDisplayedArea(void) 
    17832144        return;
    17842145
    17852146    QRect bounding  = visible.boundingRect();
     2147    LOG(VB_VBI, LOG_DEBUG,
     2148        QString("Visible bounds=%1x%2@%3,%4")
     2149        .arg(bounding.width()).arg(bounding.height())
     2150        .arg(bounding.x()).arg(bounding.y()));
     2151
    17862152    bounding = bounding.translated(m_safeArea.topLeft());
     2153    LOG(VB_VBI, LOG_DEBUG,
     2154        QString("Translated bounds=%1x%2@%3,%4")
     2155        .arg(bounding.width()).arg(bounding.height())
     2156        .arg(bounding.x()).arg(bounding.y()));
     2157
    17872158    bounding = m_safeArea.intersected(bounding);
    17882159    int left = m_safeArea.left() - bounding.left();
    17892160    int top  = m_safeArea.top()  - bounding.top();
     2161    LOG(VB_VBI, LOG_DEBUG,
     2162        QString("Intersected bounds=%1x%2@%3,%4 left=%5 top=%6")
     2163        .arg(bounding.width()).arg(bounding.height())
     2164        .arg(bounding.x()).arg(bounding.y())
     2165        .arg(left).arg(top));
    17902166    SetArea(MythRect(bounding));
    17912167
    17922168    i.toFront();
  • mythtv/libs/libmythtv/subtitlescreen.h

    diff --git a/mythtv/libs/libmythtv/subtitlescreen.h b/mythtv/libs/libmythtv/subtitlescreen.h
    index 4583a0b..2dc52c1 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 method until teletextscreen.cpp is refactored into
    201203    // subtitlescreen.cpp
    private: 
    229231    QRect              m_safeArea;
    230232    QRegExp            m_removeHTML;
    231233    int                m_subtitleType;
    232     int                m_fontSize;
    233234    int                m_textFontZoom; // valid for 708 & text subs
    234235    int                m_textFontZoomPrev;
    235236    int                m_textFontDelayMs; // valid for text subs