Ticket #6748: mythtv-0.22-ticket6748.patch

File mythtv-0.22-ticket6748.patch, 20.7 KB (added by warpme@…, 14 years ago)

Patch for 0.22-fixes with fixed DVD support and proper support for !1080 resolutions

  • libs/libavcodec/allcodecs.c

    diff -Naur mythtv-0.22-23082-old/libs/libavcodec/allcodecs.c mythtv-0.22-23082-new/libs/libavcodec/allcodecs.c
    old new  
    302302    /* subtitles */
    303303    REGISTER_ENCDEC  (DVBSUB, dvbsub);
    304304    REGISTER_ENCDEC  (DVDSUB, dvdsub);
     305    REGISTER_DECODER (PGSSUB, pgssub);
    305306    REGISTER_ENCDEC  (XSUB, xsub);
    306307
    307308    /* external libraries */
  • libs/libavcodec/avcodec.h

    diff -Naur mythtv-0.22-23082-old/libs/libavcodec/avcodec.h mythtv-0.22-23082-new/libs/libavcodec/avcodec.h
    old new  
    330330    CODEC_ID_XSUB,
    331331    CODEC_ID_SSA,
    332332    CODEC_ID_MOV_TEXT,
     333    CODEC_ID_HDMV_PGS_SUBTITLE,
    333334
    334335    /* teletext codecs */
    335336    CODEC_ID_MPEG2VBI,
  • libs/libavcodec/libavcodec.pro

    diff -Naur mythtv-0.22-23082-old/libs/libavcodec/libavcodec.pro mythtv-0.22-23082-new/libs/libavcodec/libavcodec.pro
    old new  
    222222contains( CONFIG_PGM_ENCODER, yes )             { SOURCES *= pnmenc.c }
    223223contains( CONFIG_PGMYUV_DECODER, yes )          { SOURCES *= pnmenc.c pnm.c }
    224224contains( CONFIG_PGMYUV_ENCODER, yes )          { SOURCES *= pnmenc.c }
     225contains( CONFIG_PGSSUB_DECODER, yes )          { SOURCES *= pgssubdec.c }
    225226contains( CONFIG_PNG_DECODER, yes )             { SOURCES *= png.c pngdec.c }
    226227contains( CONFIG_PNG_ENCODER, yes )             { SOURCES *= png.c pngenc.c }
    227228contains( CONFIG_PPM_DECODER, yes )             { SOURCES *= pnmenc.c pnm.c }
  • libs/libavcodec/myth_utils.c

    diff -Naur mythtv-0.22-23082-old/libs/libavcodec/myth_utils.c mythtv-0.22-23082-new/libs/libavcodec/myth_utils.c
    old new  
    202202            /* subtitle codecs */
    203203        case CODEC_ID_DVD_SUBTITLE:     return "DVD_SUBTITLE";
    204204        case CODEC_ID_DVB_SUBTITLE:     return "DVB_SUBTITLE";
     205        case CODEC_ID_HDMV_PGS_SUBTITLE:
     206             return "HDMV_PGS_SUBTITLE";
    205207
    206208        case CODEC_ID_MPEG2VBI:         return "MPEG2VBI";
    207209        case CODEC_ID_DVB_VBI:          return "DVB_VBI";
  • libs/libavcodec/pgssubdec.c

    diff -Naur mythtv-0.22-23082-old/libs/libavcodec/pgssubdec.c mythtv-0.22-23082-new/libs/libavcodec/pgssubdec.c
    old new  
     1/*
     2 * PGS subtitle decoder
     3 * Copyright (c) 2009 Stephen Backway
     4 *
     5 * This file is part of FFmpeg.
     6 *
     7 * FFmpeg is free software; you can redistribute it and/or
     8 * modify it under the terms of the GNU Lesser General Public
     9 * License as published by the Free Software Foundation; either
     10 * version 2.1 of the License, or (at your option) any later version.
     11 *
     12 * FFmpeg is distributed in the hope that it will be useful,
     13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15 * Lesser General Public License for more details.
     16 *
     17 * You should have received a copy of the GNU Lesser General Public
     18 * License along with FFmpeg; if not, write to the Free Software
     19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     20 */
     21
     22/**
     23 * @file libavcodec/pgssubdec.c
     24 * PGS subtitle decoder
     25 */
     26
     27#include "avcodec.h"
     28#include "dsputil.h"
     29#include "colorspace.h"
     30#include "bytestream.h"
     31
     32//#define DEBUG_PACKET_CONTENTS
     33
     34#define cm  (ff_cropTbl + MAX_NEG_CROP)
     35#define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
     36
     37enum SegmentType {
     38    PALETTE_SEGMENT      = 0x14,
     39    PICTURE_SEGMENT      = 0x15,
     40    PRESENTATION_SEGMENT = 0x16,
     41    WINDOW_SEGMENT       = 0x17,
     42    DISPLAY_SEGMENT      = 0x80,
     43};
     44
     45typedef struct PGSSubPresentation {
     46    int x;
     47    int y;
     48    int video_w;
     49    int video_h;
     50    int id_number;
     51} PGSSubPresentation;
     52
     53typedef struct PGSSubPicture {
     54    int          w;
     55    int          h;
     56    uint8_t      *rle;
     57    unsigned int rle_size, rle_len;
     58} PGSSubPicture;
     59
     60typedef struct PGSSubContext {
     61    PGSSubPresentation presentation;
     62    uint32_t           clut[256];
     63    PGSSubPicture      picture;
     64} PGSSubContext;
     65
     66static av_cold int init_decoder(AVCodecContext *avctx)
     67{
     68    avctx->pix_fmt     = PIX_FMT_RGB32;
     69
     70    return 0;
     71}
     72
     73static av_cold int close_decoder(AVCodecContext *avctx)
     74{
     75    PGSSubContext *ctx = avctx->priv_data;
     76
     77    av_freep(&ctx->picture.rle);
     78    ctx->picture.rle_size  = 0;
     79
     80    return 0;
     81}
     82
     83/**
     84 * Decodes the RLE data.
     85 *
     86 * The subtitle is stored as an Run Length Encoded image.
     87 *
     88 * @param avctx contains the current codec context
     89 * @param sub pointer to the processed subtitle data
     90 * @param buf pointer to the RLE data to process
     91 * @param buf_size size of the RLE data to process
     92 */
     93static int decode_rle(AVCodecContext *avctx, AVSubtitle *sub,
     94                      const uint8_t *buf, unsigned int buf_size)
     95{
     96    uint8_t *rle_bitmap_end;
     97    int pixel_count, line_count;
     98
     99    rle_bitmap_end = buf + buf_size;
     100
     101    sub->rects[0]->pict.data[0] = av_malloc(sub->rects[0]->w * sub->rects[0]->h);
     102
     103    if (!sub->rects[0]->pict.data[0])
     104        return -1;
     105
     106    pixel_count = 0;
     107    line_count  = 0;
     108
     109    while (buf < rle_bitmap_end && line_count < sub->rects[0]->h) {
     110        uint8_t flags, color;
     111        int run;
     112
     113        color = bytestream_get_byte(&buf);
     114        run   = 1;
     115
     116        if (color == 0x00) {
     117            flags = bytestream_get_byte(&buf);
     118            run   = flags & 0x3f;
     119            if (flags & 0x40)
     120                run = (run << 8) + bytestream_get_byte(&buf);
     121            color = flags & 0x80 ? bytestream_get_byte(&buf) : 0;
     122        }
     123
     124        if (run > 0 && pixel_count + run <= sub->rects[0]->w * sub->rects[0]->h) {
     125            memset(sub->rects[0]->pict.data[0] + pixel_count, color, run);
     126            pixel_count += run;
     127        } else if (run == 0) {
     128            /*
     129             * New Line. Check if correct pixels decoded,
     130             * if not display warning and adjust bitmap
     131             * pointer to correct new line position.
     132             */
     133            if (pixel_count % sub->rects[0]->w > 0)
     134                av_log(avctx, AV_LOG_ERROR, "Decoded %d pixels, when line should be %d pixels\n",
     135                       pixel_count % sub->rects[0]->w, sub->rects[0]->w);
     136            line_count++;
     137        }
     138    }
     139
     140    dprintf(avctx, "Pixel Count = %d, Area = %d\n", pixel_count, sub->rects[0]->w * sub->rects[0]->h);
     141
     142    return 0;
     143}
     144
     145/**
     146 * Parses the picture segment packet.
     147 *
     148 * The picture segment contains details on the sequence id,
     149 * width, height and Run Length Encoded (RLE) bitmap data.
     150 *
     151 * @param avctx contains the current codec context
     152 * @param buf pointer to the packet to process
     153 * @param buf_size size of packet to process
     154 * @todo TODO: Enable support for RLE data over multiple packets
     155 */
     156static int parse_picture_segment(AVCodecContext *avctx,
     157                                  const uint8_t *buf, int buf_size)
     158{
     159    PGSSubContext *ctx = avctx->priv_data;
     160
     161    uint8_t sequence_desc;
     162    unsigned int rle_bitmap_len, width, height;
     163
     164    /* skip 3 unknown bytes: Object ID (2 bytes), Version Number */
     165    buf += 3;
     166
     167    /* Read the Sequence Description to determine if start of RLE data or appended to previous RLE */
     168    sequence_desc = bytestream_get_byte(&buf);
     169
     170    if (!(sequence_desc & 0x80)) {
     171        av_log(avctx, AV_LOG_ERROR, "Decoder does not support object data over multiple packets.\n");
     172        return -1;
     173    }
     174
     175    /* Decode rle bitmap length */
     176    rle_bitmap_len = bytestream_get_be24(&buf);
     177
     178    /* Check to ensure we have enough data for rle_bitmap_length if just a single packet */
     179    if (rle_bitmap_len > buf_size - 7) {
     180        av_log(avctx, AV_LOG_ERROR, "Not enough RLE data for specified length of %d.\n", rle_bitmap_len);
     181        return -1;
     182    }
     183
     184    ctx->picture.rle_len = rle_bitmap_len;
     185
     186    /* Get bitmap dimensions from data */
     187    width  = bytestream_get_be16(&buf);
     188    height = bytestream_get_be16(&buf);
     189
     190    /* Make sure the bitmap is not too large */
     191    if (ctx->presentation.video_w < width || ctx->presentation.video_h < height) {
     192        av_log(avctx, AV_LOG_ERROR, "Bitmap dimensions larger then video.\n");
     193        return -1;
     194    }
     195
     196    ctx->picture.w = width;
     197    ctx->picture.h = height;
     198
     199    av_fast_malloc(&ctx->picture.rle, &ctx->picture.rle_size, rle_bitmap_len);
     200
     201    if (!ctx->picture.rle)
     202        return -1;
     203
     204    memcpy(ctx->picture.rle, buf, rle_bitmap_len);
     205
     206    return 0;
     207}
     208
     209/**
     210 * Parses the palette segment packet.
     211 *
     212 * The palette segment contains details of the palette,
     213 * a maximum of 256 colors can be defined.
     214 *
     215 * @param avctx contains the current codec context
     216 * @param buf pointer to the packet to process
     217 * @param buf_size size of packet to process
     218 */
     219static void parse_palette_segment(AVCodecContext *avctx,
     220                                  const uint8_t *buf, int buf_size)
     221{
     222    PGSSubContext *ctx = avctx->priv_data;
     223
     224    const uint8_t *buf_end = buf + buf_size;
     225    int color_id;
     226    int y, cb, cr, alpha;
     227    int r, g, b, r_add, g_add, b_add;
     228
     229    /* Skip two null bytes */
     230    buf += 2;
     231
     232    while (buf < buf_end) {
     233        color_id  = bytestream_get_byte(&buf);
     234        y         = bytestream_get_byte(&buf);
     235        cb        = bytestream_get_byte(&buf);
     236        cr        = bytestream_get_byte(&buf);
     237        alpha     = bytestream_get_byte(&buf);
     238
     239        YUV_TO_RGB1(cb, cr);
     240        YUV_TO_RGB2(r, g, b, y);
     241
     242        dprintf(avctx, "Color %d := (%d,%d,%d,%d)\n", color_id, r, g, b, alpha);
     243
     244        /* Store color in palette */
     245        ctx->clut[color_id] = RGBA(r,g,b,alpha);
     246    }
     247}
     248
     249/**
     250 * Parses the presentation segment packet.
     251 *
     252 * The presentation segment contains details on the video
     253 * width, video height, x & y subtitle position.
     254 *
     255 * @param avctx contains the current codec context
     256 * @param buf pointer to the packet to process
     257 * @param buf_size size of packet to process
     258 * @todo TODO: Implement cropping
     259 * @todo TODO: Implement forcing of subtitles
     260 * @todo TODO: Blanking of subtitle
     261 */
     262static void parse_presentation_segment(AVCodecContext *avctx,
     263                                       const uint8_t *buf, int buf_size)
     264{
     265    PGSSubContext *ctx = avctx->priv_data;
     266
     267    int x, y;
     268    uint8_t block;
     269
     270    ctx->presentation.video_w = bytestream_get_be16(&buf);
     271    ctx->presentation.video_h = bytestream_get_be16(&buf);
     272
     273    dprintf(avctx, "Video Dimensions %dx%d\n",
     274            ctx->presentation.video_w, ctx->presentation.video_h);
     275
     276    /* Skip 1 bytes of unknown, frame rate? */
     277    buf++;
     278
     279    ctx->presentation.id_number = bytestream_get_be16(&buf);
     280
     281    /* Next byte is the state. */
     282    block = bytestream_get_byte(&buf);;
     283    if (block == 0x80) {
     284        /*
     285         * Skip 7 bytes of unknown:
     286         *     palette_update_flag (0x80),
     287         *     palette_id_to_use,
     288         *     Object Number (if > 0 determines if more data to process),
     289         *     object_id_ref (2 bytes),
     290         *     window_id_ref,
     291         *     composition_flag (0x80 - object cropped, 0x40 - object forced)
     292         */
     293        buf += 7;
     294
     295        x = bytestream_get_be16(&buf);
     296        y = bytestream_get_be16(&buf);
     297
     298        /* TODO If cropping, cropping_x, cropping_y, cropping_width, cropping_height (all 2 bytes).*/
     299
     300        dprintf(avctx, "Subtitle Placement x=%d, y=%d\n", x, y);
     301
     302        if (x > ctx->presentation.video_w || y > ctx->presentation.video_h) {
     303            av_log(avctx, AV_LOG_ERROR, "Subtitle out of video bounds. x = %d, y = %d, video width = %d, video height = %d.\n",
     304                   x, y, ctx->presentation.video_w, ctx->presentation.video_h);
     305            x = 0; y = 0;
     306        }
     307
     308        /* Fill in dimensions */
     309        ctx->presentation.x = x;
     310        ctx->presentation.y = y;
     311    } else if (block == 0x00) {
     312        /* TODO: Blank context as subtitle should not be displayed.
     313         *       If the subtitle is blanked now the subtitle is not
     314         *       on screen long enough to read, due to a delay in
     315         *       initial display timing.
     316         */
     317    }
     318}
     319
     320/**
     321 * Parses the display segment packet.
     322 *
     323 * The display segment controls the updating of the display.
     324 *
     325 * @param avctx contains the current codec context
     326 * @param data pointer to the data pertaining the subtitle to display
     327 * @param buf pointer to the packet to process
     328 * @param buf_size size of packet to process
     329 * @todo TODO: Fix start time, relies on correct PTS, currently too late
     330 *
     331 * @todo TODO: Fix end time, normally cleared by a second display
     332 * @todo       segment, which is currently ignored as it clears
     333 * @todo       the subtitle too early.
     334 */
     335static int display_end_segment(AVCodecContext *avctx, void *data,
     336                               const uint8_t *buf, int buf_size)
     337{
     338    AVSubtitle    *sub = data;
     339    PGSSubContext *ctx = avctx->priv_data;
     340
     341    /*
     342     *      The end display time is a timeout value and is only reached
     343     *      if the next subtitle is later then timeout or subtitle has
     344     *      not been cleared by a subsequent empty display command.
     345     */
     346
     347    sub->start_display_time = 0;
     348    sub->end_display_time   = 20000;
     349    sub->format             = 0;
     350
     351    if (!sub->rects) {
     352        sub->rects     = av_mallocz(sizeof(*sub->rects));
     353        sub->rects[0]  = av_mallocz(sizeof(*sub->rects[0]));
     354        sub->num_rects = 1;
     355    }
     356
     357    sub->rects[0]->x    = ctx->presentation.x;
     358    sub->rects[0]->y    = ctx->presentation.y;
     359    sub->rects[0]->w    = ctx->picture.w;
     360    sub->rects[0]->h    = ctx->picture.h;
     361    sub->rects[0]->type = SUBTITLE_BITMAP;
     362
     363    /* Process bitmap */
     364    sub->rects[0]->pict.linesize[0] = ctx->picture.w;
     365
     366    if (ctx->picture.rle)
     367        if(decode_rle(avctx, sub, ctx->picture.rle, ctx->picture.rle_len) < 0)
     368            return 0;
     369
     370    /* Allocate memory for colors */
     371    sub->rects[0]->nb_colors    = 256;
     372    sub->rects[0]->pict.data[1] = av_malloc(sub->rects[0]->nb_colors * sizeof(uint32_t));
     373
     374    memcpy(sub->rects[0]->pict.data[1], ctx->clut, sub->rects[0]->nb_colors * sizeof(uint32_t));
     375
     376    return 1;
     377}
     378
     379static int decode(AVCodecContext *avctx, void *data, int *data_size,
     380                  AVPacket *avpkt)
     381{
     382    const uint8_t *buf = avpkt->data;
     383    int buf_size       = avpkt->size;
     384
     385    const uint8_t *buf_end;
     386    uint8_t       segment_type;
     387    int           segment_length;
     388
     389#ifdef DEBUG_PACKET_CONTENTS
     390    int i;
     391
     392    av_log(avctx, AV_LOG_INFO, "PGS sub packet:\n");
     393
     394    for (i = 0; i < buf_size; i++) {
     395        av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]);
     396        if (i % 16 == 15)
     397            av_log(avctx, AV_LOG_INFO, "\n");
     398    }
     399
     400    if (i & 15)
     401        av_log(avctx, AV_LOG_INFO, "\n");
     402#endif
     403
     404    *data_size = 0;
     405
     406    /* Ensure that we have received at a least a segment code and segment length */
     407    if (buf_size < 3)
     408        return -1;
     409
     410    buf_end = buf + buf_size;
     411
     412    /* Step through buffer to identify segments */
     413    while (buf < buf_end) {
     414        segment_type   = bytestream_get_byte(&buf);
     415        segment_length = bytestream_get_be16(&buf);
     416
     417        dprintf(avctx, "Segment Length %d, Segment Type %x\n", segment_length, segment_type);
     418
     419        if (segment_type != DISPLAY_SEGMENT && segment_length > buf_end - buf)
     420            break;
     421
     422        switch (segment_type) {
     423        case PALETTE_SEGMENT:
     424            parse_palette_segment(avctx, buf, segment_length);
     425            break;
     426        case PICTURE_SEGMENT:
     427            parse_picture_segment(avctx, buf, segment_length);
     428            break;
     429        case PRESENTATION_SEGMENT:
     430            parse_presentation_segment(avctx, buf, segment_length);
     431            break;
     432        case WINDOW_SEGMENT:
     433            /*
     434             * Window Segment Structure (No new information provided):
     435             *     2 bytes: Unkown,
     436             *     2 bytes: X position of subtitle,
     437             *     2 bytes: Y position of subtitle,
     438             *     2 bytes: Width of subtitle,
     439             *     2 bytes: Height of subtitle.
     440             */
     441            break;
     442        case DISPLAY_SEGMENT:
     443            *data_size = display_end_segment(avctx, data, buf, segment_length);
     444            break;
     445        default:
     446            av_log(avctx, AV_LOG_ERROR, "Unknown subtitle segment type 0x%x, length %d\n",
     447                   segment_type, segment_length);
     448            break;
     449        }
     450
     451        buf += segment_length;
     452    }
     453
     454    return buf_size;
     455}
     456
     457AVCodec pgssub_decoder = {
     458    "pgssub",
     459    CODEC_TYPE_SUBTITLE,
     460    CODEC_ID_HDMV_PGS_SUBTITLE,
     461    sizeof(PGSSubContext),
     462    init_decoder,
     463    NULL,
     464    close_decoder,
     465    decode,
     466    .long_name = NULL_IF_CONFIG_SMALL("HDMV Presentation Graphic Stream subtitles"),
     467};
  • libs/libavformat/mpegts.c

    diff -Naur mythtv-0.22-23082-old/libs/libavformat/mpegts.c mythtv-0.22-23082-new/libs/libavformat/mpegts.c
    old new  
    578578    { 0x84, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
    579579    { 0x85, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
    580580    { 0x86, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
     581    { 0x90, CODEC_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE },
    581582    { 0 },
    582583};
    583584
     
    10111012            //case STREAM_TYPE_PRIVATE_DATA:
    10121013        case STREAM_TYPE_VBI_DVB:
    10131014        case STREAM_TYPE_SUBTITLE_DVB:
     1015        case STREAM_TYPE_SUBTITLE_PGS:
    10141016        case STREAM_TYPE_DSMCC_B:
    10151017            val = 1;
    10161018            break;
     
    16001602            codec_type = CODEC_TYPE_SUBTITLE;
    16011603            codec_id = CODEC_ID_DVB_SUBTITLE;
    16021604            break;
     1605        case STREAM_TYPE_SUBTITLE_PGS:
     1606            codec_type = CODEC_TYPE_SUBTITLE;
     1607            codec_id = CODEC_ID_HDMV_PGS_SUBTITLE;
     1608            break;
    16031609        case STREAM_TYPE_DSMCC_B:
    16041610            codec_type = CODEC_TYPE_DATA;
    16051611            codec_id = CODEC_ID_DSMCC_B;
  • libs/libavformat/mpegts.h

    diff -Naur mythtv-0.22-23082-old/libs/libavformat/mpegts.h mythtv-0.22-23082-new/libs/libavformat/mpegts.h
    old new  
    6969#define STREAM_TYPE_AUDIO_HDMV_DTS_HD_MASTER 0x86
    7070
    7171#define STREAM_TYPE_SUBTITLE_DVB    0x100
     72#define STREAM_TYPE_SUBTITLE_PGS    0x90
    7273#define STREAM_TYPE_VBI_DVB         0x101
    7374
    7475typedef struct MpegTSContext MpegTSContext;
  • libs/libmythtv/NuppelVideoPlayer.cpp

    diff -Naur mythtv-0.22-23082-old/libs/libmythtv/NuppelVideoPlayer.cpp mythtv-0.22-23082-new/libs/libmythtv/NuppelVideoPlayer.cpp
    old new  
    72147214
    72157215                // scale the subtitle images which are scaled and positioned for
    72167216                // a 720x576 video resolution to fit the current OSD resolution
    7217                 float vsize = 576.0;
    7218                 if (player_ctx->buffer->isDVD())
    7219                     vsize = (float) video_disp_dim.height();
    7220 
    7221                 float hmult = osd->GetSubtitleBounds().width() / 720.0;
    7222                 float vmult = osd->GetSubtitleBounds().height() / vsize;
    7223 
    7224                 rect->x = (int)(rect->x * hmult);
    7225                 rect->y = (int)(rect->y * vmult);
    7226                 rect->w = (int)(rect->w * hmult);
    7227                 rect->h = (int)(rect->h * vmult);
     7217                float vsize = (float) video_disp_dim.height();
     7218                float hsize = (float) video_disp_dim.width();
     7219   
     7220                if (player_ctx->buffer->isDVD())
     7221                    hsize = 720.0;
    72287222
    7229                 if (hmult < 0.98 || hmult > 1.02 || vmult < 0.98 || hmult > 1.02)
     7223                float hmult = osd->GetSubtitleBounds().width() / hsize;
     7224                float vmult = osd->GetSubtitleBounds().height() / vsize;               
     7225                                               
     7226                rect->x = (int)(rect->x * hmult);
     7227                rect->y = (int)(rect->y * vmult);
     7228                rect->w = (int)(rect->w * hmult);
     7229                rect->h = (int)(rect->h * vmult);
     7230               
     7231                if (hmult < 0.98 || hmult > 1.02 || vmult < 0.98 || vmult > 1.02)
    72307232                    qImage = qImage.scaled(rect->w, rect->h,
    72317233                            Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    72327234