Subversion Repositories tpanel

Rev

Rev 449 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
2
 * Copyright (C) 2020 to 2023 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <exception>
20
 
21
#include <include/core/SkFont.h>
22
#include <include/core/SkFontMetrics.h>
23
#include <include/core/SkTextBlob.h>
24
 
25
#include "tresources.h"
26
#include "texpat++.h"
27
#include "tpagemanager.h"
28
#include "tsubpage.h"
29
#include "tdrawimage.h"
30
#include "tconfig.h"
31
#include "terror.h"
32
#if TESTMODE == 1
33
#include "testmode.h"
34
#endif
35
 
36
#if __cplusplus < 201402L
37
#   error "This module requires at least C++14 standard!"
38
#else
39
#   if __cplusplus < 201703L
40
#       include <experimental/filesystem>
41
namespace fs = std::experimental::filesystem;
42
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
43
#   else
44
#       include <filesystem>
45
#       ifdef __ANDROID__
46
namespace fs = std::__fs::filesystem;
47
#       else
48
namespace fs = std::filesystem;
49
#       endif
50
#   endif
51
#endif
52
 
53
using std::string;
54
using std::vector;
55
using namespace Button;
56
using namespace Expat;
57
 
58
extern TPageManager *gPageManager;
59
 
60
TSubPage::TSubPage(const string& name)
61
    : mFile(name)
62
{
63
    DECL_TRACER("TSubPage::TSubPage(const string& path)");
64
    TError::clear();
65
 
449 andreas 66
    string projectPath = TConfig::getProjectPath();
446 andreas 67
 
68
    if (!fs::exists(projectPath + "/prj.xma"))
69
    {
70
        MSG_ERROR("Directory " << projectPath << " doesn't exist!");
71
        return;
72
    }
73
 
74
    string path = makeFileName(projectPath, name);
75
 
76
    if (isValidFile())
77
        mFName = getFileName();
78
    else
79
    {
80
        MSG_ERROR("Either the path \"" << projectPath << "\" or the file name \"" << name << "\" is invalid!");
81
        TError::setError();
82
        return;
83
    }
84
 
85
    if (gPageManager)
86
    {
87
        if (!_displayButton)
88
            _displayButton = gPageManager->getCallbackDB();
89
 
90
        if (!_setBackground)
91
            _setBackground = gPageManager->getCallbackBG();
92
 
93
        if (!_playVideo)
94
            _playVideo = gPageManager->getCallbackPV();
95
 
96
        if (!_callDropSubPage)
97
            _callDropSubPage = gPageManager->getCallDropSubPage();
98
    }
99
 
100
    initialize();
101
}
102
 
103
TSubPage::~TSubPage()
104
{
105
    DECL_TRACER("TSubPage::~TSubPage()");
106
 
107
    if (mSubpage.name.empty())
108
    {
109
        MSG_WARNING("Invalid page found!");
110
        return;
111
    }
112
 
113
    MSG_DEBUG("Destroing subpage " << mSubpage.pageID << ": " << mSubpage.name);
114
 
115
    BUTTONS_T *b = TPageInterface::getButtons();
116
    BUTTONS_T *next = nullptr;
117
 
118
    while (b)
119
    {
120
        next = b->next;
121
 
122
        if (b->button)
123
        {
124
            // If this is a system button, we must remove it first from the list
125
            dropButton(b->button);
126
            delete b->button;
127
        }
128
 
129
        delete b;
130
        b = next;
131
    }
132
 
133
    setButtons(nullptr);
134
}
135
 
136
void TSubPage::initialize()
137
{
138
    DECL_TRACER("TSubPage::initialize()");
139
 
140
    if (mFName.empty())
141
        return;
142
 
143
    TError::clear();
144
    TExpat xml(mFName);
145
    xml.setEncoding(ENC_CP1250);
146
 
147
    if (!xml.parse())
148
        return;
149
 
150
    int depth = 0;
151
    size_t index = 0;
152
    size_t oldIndex = 0;
153
    string ename, content;
154
    vector<ATTRIBUTE_t> attrs;
155
 
156
    if ((index = xml.getElementIndex("page", &depth)) == TExpat::npos)
157
    {
158
        MSG_ERROR("Element \"page\" with attribute \"type\" was not found! Invalid XML file!");
159
        TError::setError();
160
        return;
161
    }
162
 
163
    attrs = xml.getAttributes();
164
    string stype = xml.getAttribute("type", attrs);
165
 
166
    if (stype.compare("subpage") != 0)
167
    {
168
        MSG_ERROR("The type " << stype << " is invalid for a subpage!");
169
        TError::setError();
170
        return;
171
    }
172
 
173
    mSubpage.popupType = xml.getAttribute("popupType", attrs);  // popup or subpage
174
 
175
    while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
176
    {
177
        if (ename.compare("pageID") == 0)
178
            mSubpage.pageID = xml.convertElementToInt(content);
179
        else if (ename.compare("name") == 0)
180
            mSubpage.name = content;
181
        else if (ename.compare("left") == 0)
182
        {
183
            mSubpage.left = xml.convertElementToInt(content);
184
            mSubpage.leftOrig = mSubpage.left;
185
        }
186
        else if (ename.compare("top") == 0)
187
        {
188
            mSubpage.top = xml.convertElementToInt(content);
189
            mSubpage.topOrig = mSubpage.top;
190
        }
191
        else if (ename.compare("width") == 0)
192
        {
193
            mSubpage.width = xml.convertElementToInt(content);
194
            mSubpage.widthOrig = mSubpage.width;
195
        }
196
        else if (ename.compare("height") == 0)
197
        {
198
            mSubpage.height = xml.convertElementToInt(content);
199
            mSubpage.heightOrig = mSubpage.height;
200
        }
201
        else if (ename.compare("group") == 0)
202
            mSubpage.group = content;
203
        else if (ename.compare("modal") == 0)
204
            mSubpage.modal = xml.convertElementToInt(content);
205
        else if (ename.compare("showEffect") == 0)
206
            mSubpage.showEffect = (SHOWEFFECT)xml.convertElementToInt(content);
207
        else if (ename.compare("showTime") == 0)
208
            mSubpage.showTime = xml.convertElementToInt(content);
209
        else if (ename.compare("hideTime") == 0)
210
            mSubpage.hideTime = xml.convertElementToInt(content);
211
        else if (ename.compare("hideEffect") == 0)
212
            mSubpage.hideEffect = (SHOWEFFECT)xml.convertElementToInt(content);
213
        else if (ename.compare("timeout") == 0)
214
            mSubpage.timeout = xml.convertElementToInt(content);
215
        else if (ename.compare("resetPos") == 0)
216
            mSubpage.resetPos = xml.convertElementToInt(content);
217
        else if (ename.compare("button") == 0)      // Read a button
218
        {
219
            try
220
            {
221
                TButton *button = new TButton();
222
                TPageInterface::registerListCallback<TSubPage>(button, this);
223
                button->setPalette(mPalette);
224
                button->setFonts(getFonts());
225
                index = button->initialize(&xml, index);
226
                button->setParentSize(mSubpage.width, mSubpage.height);
227
                button->registerCallback(_displayButton);
228
                button->regCallPlayVideo(_playVideo);
229
 
230
                if (TError::isError())
231
                {
232
                    MSG_ERROR("Dropping button because of previous errors!");
233
                    delete button;
234
                    return;
235
                }
236
 
237
                button->setHandle(((mSubpage.pageID << 16) & 0xffff0000) | button->getButtonIndex());
238
                button->createButtons();
239
                addButton(button);
240
                index++;    // Jump over the end tag of the button.
241
            }
242
            catch (std::exception& e)
243
            {
244
                MSG_ERROR("Memory exception: " << e.what());
245
                TError::setError();
246
                return;
247
            }
248
        }
249
        else if (ename.compare("sr") == 0)
250
        {
251
            SR_T sr;
252
            sr.number = xml.getAttributeInt("number", attrs);
253
 
254
            while ((index = xml.getNextElementFromIndex(index, &ename, &content, &attrs)) != TExpat::npos)
255
            {
256
                if (ename.compare("bs") == 0)
257
                    sr.bs = content;
258
                else if (ename.compare("cb") == 0)
259
                    sr.cb = content;
260
                else if (ename.compare("cf") == 0)
261
                    sr.cf = content;
262
                else if (ename.compare("ct") == 0)
263
                    sr.ct = content;
264
                else if (ename.compare("ec") == 0)
265
                    sr.ec = content;
266
                else if (ename.compare("bm") == 0)
267
                    sr.bm = content;
268
                else if (ename.compare("mi") == 0)
269
                    sr.mi = content;
270
                else if (ename.compare("ji") == 0)
271
                    sr.ji = xml.convertElementToInt(content);
272
                else if (ename.compare("jb") == 0)
273
                    sr.jb = xml.convertElementToInt(content);
274
                else if (ename.compare("fi") == 0)
275
                    sr.fi = xml.convertElementToInt(content);
276
                else if (ename.compare("ii") == 0)
277
                    sr.ii = xml.convertElementToInt(content);
278
                else if (ename.compare("ix") == 0)
279
                    sr.ix = xml.convertElementToInt(content);
280
                else if (ename.compare("iy") == 0)
281
                    sr.iy = xml.convertElementToInt(content);
282
                else if (ename.compare("oo") == 0)
283
                    sr.oo = xml.convertElementToInt(content);
284
                else if (ename.compare("te") == 0)
285
                    sr.te = content;
286
                else if (ename.compare("tx") == 0)
287
                    sr.tx = xml.convertElementToInt(content);
288
                else if (ename.compare("ty") == 0)
289
                    sr.ty = xml.convertElementToInt(content);
290
                else if (ename.compare("et") == 0)
291
                    sr.et = xml.convertElementToInt(content);
292
                else if (ename.compare("ww") == 0)
293
                    sr.ww = xml.convertElementToInt(content);
294
                else if (ename.compare("jt") == 0)
479 andreas 295
                    sr.jt = (Button::ORIENTATION)xml.convertElementToInt(content);
446 andreas 296
 
297
                oldIndex = index;
298
            }
299
 
300
            mSubpage.sr.push_back(sr);
301
        }
302
 
303
        if (index == TExpat::npos)
304
            index = oldIndex + 1;
305
    }
306
 
307
    setSR(mSubpage.sr);
308
/*
309
    if (TStreamError::checkFilter(HLOG_DEBUG))
310
    {
311
        MSG_DEBUG("PageID: " << mSubpage.pageID);
312
        MSG_DEBUG("Name  : " << mSubpage.name);
313
        MSG_DEBUG("Left  : " << mSubpage.left);
314
        MSG_DEBUG("Top   : " << mSubpage.top);
315
        MSG_DEBUG("Width : " << mSubpage.width);
316
        MSG_DEBUG("Height: " << mSubpage.height);
317
 
318
        vector<SR_T>::iterator iter;
319
        size_t pos = 1;
320
 
321
        for (iter = mSubpage.sr.begin(); iter != mSubpage.sr.end(); ++iter)
322
        {
323
            MSG_DEBUG("   " << pos << ": id: " << iter->number);
324
            MSG_DEBUG("   " << pos << ": bs: " << iter->bs);
325
            MSG_DEBUG("   " << pos << ": cb: " << iter->cb);
326
            MSG_DEBUG("   " << pos << ": cf: " << iter->cf);
327
            MSG_DEBUG("   " << pos << ": ct: " << iter->ct);
328
            MSG_DEBUG("   " << pos << ": ec: " << iter->ec);
329
            MSG_DEBUG("   " << pos << ": bm: " << iter->bm);
330
            MSG_DEBUG("   " << pos << ": mi: " << iter->mi);
331
            MSG_DEBUG("   " << pos << ": fi: " << iter->fi);
332
            pos++;
333
        }
334
    }
335
*/
336
    // Here the sort function could be called. But it's not necessary because
337
    // the buttons are stored in ascending Z order. Therefor the following
338
    // method call is commented out.
339
    // sortButtons();
340
}
341
 
342
void TSubPage::show()
343
{
344
    DECL_TRACER("TSubPage::show()");
345
 
346
    if (!_setBackground)
347
    {
348
        if (gPageManager && gPageManager->getCallbackBG())
349
            _setBackground = gPageManager->getCallbackBG();
350
        else
351
        {
352
            MSG_WARNING("No callback \"setBackground\" was set!");
353
#if TESTMODE == 1
354
            setScreenDone();
355
#endif
356
            return;
357
        }
358
    }
359
 
360
    bool haveImage = false;
361
    ulong handle = (mSubpage.pageID << 16) & 0xffff0000;
362
    MSG_DEBUG("Processing subpage " << mSubpage.pageID << ": " << mSubpage.name);
363
    SkBitmap target;
364
 
365
    if (!allocPixels(mSubpage.width, mSubpage.height, &target))
366
    {
367
#if TESTMODE == 1
368
        setScreenDone();
369
#endif
370
        return;
371
    }
372
 
373
    if (mSubpage.resetPos != 0)
374
    {
375
        mSubpage.left = mSubpage.leftOrig;
376
        mSubpage.top = mSubpage.topOrig;
377
        mSubpage.width = mSubpage.widthOrig;
378
        mSubpage.height = mSubpage.heightOrig;
379
    }
380
 
381
    target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
382
    // Draw the background, if any
383
    if (mSubpage.sr.size() > 0 && (!mSubpage.sr[0].bm.empty() || !mSubpage.sr[0].mi.empty()))
384
    {
385
        TDrawImage dImage;
386
        dImage.setWidth(mSubpage.width);
387
        dImage.setHeight(mSubpage.height);
388
 
389
        if (!mSubpage.sr[0].bm.empty())
390
        {
391
            MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
392
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
393
            SkBitmap bm;
394
 
395
            if (rawImage)
396
            {
397
                MSG_DEBUG("Decoding image BM ...");
398
 
399
                if (!DecodeDataToBitmap(rawImage, &bm))
400
                {
401
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
402
                }
403
                else if (!bm.empty())
404
                {
405
                    dImage.setImageBm(bm);
406
                    SkImageInfo info = bm.info();
407
                    mSubpage.sr[0].bm_width = info.width();
408
                    mSubpage.sr[0].bm_height = info.height();
409
                    haveImage = true;
410
                }
411
                else
412
                {
413
                    MSG_WARNING("BM image " << mSubpage.sr[0].bm << " seems to be empty!");
414
                }
415
            }
416
        }
417
 
418
        if (!mSubpage.sr[0].mi.empty())
419
        {
420
            MSG_DEBUG("Loading image " << mSubpage.sr[0].mi);
421
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].mi);
422
            SkBitmap mi;
423
 
424
            if (rawImage)
425
            {
426
                MSG_DEBUG("Decoding image MI ...");
427
 
428
                if (!DecodeDataToBitmap(rawImage, &mi))
429
                {
430
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].mi);
431
                }
432
                else if (!mi.empty())
433
                {
434
                    dImage.setImageMi(mi);
435
                    SkImageInfo info = mi.info();
436
                    mSubpage.sr[0].mi_width = info.width();
437
                    mSubpage.sr[0].mi_height = info.height();
438
                    haveImage = true;
439
                }
440
                else
441
                {
442
                    MSG_WARNING("MI image " << mSubpage.sr[0].mi << " seems to be empty!");
443
                }
444
            }
445
        }
446
 
447
        if (haveImage)
448
        {
449
            dImage.setSr(mSubpage.sr);
450
 
451
            if (!dImage.drawImage(&target))
452
                return;
453
 
454
            if (!mSubpage.sr[0].te.empty())
455
            {
456
                if (!drawText(mSubpage, &target))
457
                    return;
458
            }
459
#ifdef _OPAQUE_SKIA_
460
            if (mSubpage.sr[0].oo < 255 && mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
461
                setOpacity(&target, mSubpage.sr[0].oo);
462
#endif
463
 
464
#ifdef _SCALE_SKIA_
465
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
466
            {
467
                SkPaint paint;
468
                int left, top;
469
                SkImageInfo info = target.info();
470
 
471
                paint.setBlendMode(SkBlendMode::kSrc);
472
                paint.setFilterQuality(kHigh_SkFilterQuality);
473
                // Calculate new dimension
474
                double scaleFactor = gPageManager->getScaleFactor();
475
                MSG_DEBUG("Using scale factor " << scaleFactor);
476
                int lwidth = (int)((double)info.width() * scaleFactor);
477
                int lheight = (int)((double)info.height() * scaleFactor);
478
                int twidth = (int)((double)mSubpage.width * scaleFactor);
479
                int theight = (int)((double)mSubpage.height * scaleFactor);
480
                calcPosition(lwidth, lheight, &left, &top);
481
                // Create a canvas and draw new image
482
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
483
 
484
                if (!allocPixels(twidth, theight, &target))
485
                {
486
#if TESTMODE == 1
487
                    setScreenDone();
488
#endif
489
                    return;
490
                }
491
 
492
                target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
493
                SkCanvas can(target, SkSurfaceProps());
494
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
495
                can.drawImageRect(im, rect, &paint);
496
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
497
            }
498
#endif
499
#ifdef _OPAQUE_SKIA_
500
            if (mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
501
            {
502
                TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
503
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
504
            }
505
#else
506
            if (mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
507
            {
508
                TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
509
                _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
510
            }
511
#endif
512
        }
513
    }
514
 
515
    if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].te.empty())
516
    {
517
        MSG_DEBUG("Drawing a text only on background image ...");
518
 
519
        if (drawText(mSubpage, &target))
520
            haveImage = true;
521
    }
522
 
523
    // Check for a frame and draw it if there is one.
524
    if (!mSubpage.sr[0].bs.empty())
525
    {
526
        if (drawFrame(mSubpage, &target))
527
            haveImage = true;
528
    }
529
 
530
    if (haveImage)
531
    {
532
#ifdef _OPAQUE_SKIA_
533
        if (mSubpage.sr[0].oo < 255)
534
            setOpacity(&target, mSubpage.sr[0].oo);
535
#endif
536
        TBitmap image((unsigned char *)target.getPixels(), target.info().width(), target.info().height());
537
#ifdef _OPAQUE_SKIA_
538
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf));
539
#else
540
        _setBackground(handle, image, target.info().width(), target.info().height(), TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
541
#endif
542
    }
543
    else if (mSubpage.sr.size() > 0 && !haveImage)
544
    {
545
        MSG_DEBUG("Calling \"setBackground\" with no image ...");
546
#ifdef _OPAQUE_SKIA_
547
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(mSubpage.sr[0].cf));
548
#else
549
        _setBackground(handle, TBitmap(), 0, 0, TColor::getColor(mSubpage.sr[0].cf), mSubpage.sr[0].oo);
550
#endif
551
    }
552
 
553
    // Draw the buttons
554
    BUTTONS_T *button = TPageInterface::getButtons();
555
 
556
    while (button)
557
    {
558
        if (button->button)
559
        {
560
            MSG_DEBUG("Drawing button " << handleToString(button->button->getHandle()) << ": " << button->button->getButtonIndex() << " --> " << button->button->getButtonName());
561
            TPageInterface::registerListCallback<TSubPage>(button->button, this);
562
            button->button->registerCallback(_displayButton);
563
            button->button->regCallPlayVideo(_playVideo);
564
            button->button->setFonts(getFonts());
565
            button->button->setPalette(mPalette);
566
            button->button->createButtons();
567
 
568
            if (mSubpage.sr.size() > 0)
569
                button->button->setGlobalOpacity(mSubpage.sr[0].oo);
570
 
571
            if (mSubpage.resetPos != 0)
572
                button->button->resetButton();
573
 
574
            button->button->show();
575
        }
576
 
577
        button = button->next;
578
    }
579
 
580
    // Mark page as visible
581
    mVisible = true;
582
 
583
    if (gPageManager && gPageManager->getPageFinished())
584
        gPageManager->getPageFinished()(handle);
585
}
586
 
587
SkBitmap& TSubPage::getBgImage()
588
{
589
    DECL_TRACER("TSubPage::getBgImage()");
590
 
591
    if (!mBgImage.empty())
592
        return mBgImage;
593
 
594
    bool haveImage = false;
595
    MSG_DEBUG("Creating image for subpage " << mSubpage.pageID << ": " << mSubpage.name);
596
    SkBitmap target;
597
 
598
    if (!allocPixels(mSubpage.width, mSubpage.height, &target))
599
        return mBgImage;
600
 
601
    target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
602
    // Draw the background, if any
603
    if (mSubpage.sr.size() > 0 && (!mSubpage.sr[0].bm.empty() || !mSubpage.sr[0].mi.empty()))
604
    {
605
        TDrawImage dImage;
606
        dImage.setWidth(mSubpage.width);
607
        dImage.setHeight(mSubpage.height);
608
 
609
        if (!mSubpage.sr[0].bm.empty())
610
        {
611
            MSG_DEBUG("Loading image " << mSubpage.sr[0].bm);
612
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].bm);
613
            SkBitmap bm;
614
 
615
            if (rawImage)
616
            {
617
                MSG_DEBUG("Decoding image BM ...");
618
 
619
                if (!DecodeDataToBitmap(rawImage, &bm))
620
                {
621
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].bm);
622
                }
623
                else if (!bm.empty())
624
                {
625
                    dImage.setImageBm(bm);
626
                    SkImageInfo info = bm.info();
627
                    mSubpage.sr[0].bm_width = info.width();
628
                    mSubpage.sr[0].bm_height = info.height();
629
                    haveImage = true;
630
                }
631
                else
632
                {
633
                    MSG_WARNING("BM image " << mSubpage.sr[0].bm << " seems to be empty!");
634
                }
635
            }
636
        }
637
 
638
        if (!mSubpage.sr[0].mi.empty())
639
        {
640
            MSG_DEBUG("Loading image " << mSubpage.sr[0].mi);
641
            sk_sp<SkData> rawImage = readImage(mSubpage.sr[0].mi);
642
            SkBitmap mi;
643
 
644
            if (rawImage)
645
            {
646
                MSG_DEBUG("Decoding image MI ...");
647
 
648
                if (!DecodeDataToBitmap(rawImage, &mi))
649
                {
650
                    MSG_WARNING("Problem while decoding image " << mSubpage.sr[0].mi);
651
                }
652
                else if (!mi.empty())
653
                {
654
                    dImage.setImageMi(mi);
655
                    SkImageInfo info = mi.info();
656
                    mSubpage.sr[0].mi_width = info.width();
657
                    mSubpage.sr[0].mi_height = info.height();
658
                    haveImage = true;
659
                }
660
                else
661
                {
662
                    MSG_WARNING("MI image " << mSubpage.sr[0].mi << " seems to be empty!");
663
                }
664
            }
665
        }
666
 
667
        if (haveImage)
668
        {
669
            dImage.setSr(mSubpage.sr);
670
 
671
            if (!dImage.drawImage(&target))
672
                return mBgImage;
673
 
674
            if (!mSubpage.sr[0].te.empty())
675
            {
676
                if (!drawText(mSubpage, &target))
677
                    return mBgImage;
678
            }
679
 
680
            if (mSubpage.sr[0].oo < 255 && mSubpage.sr[0].te.empty() && mSubpage.sr[0].bs.empty())
681
                setOpacity(&target, mSubpage.sr[0].oo);
682
 
683
#ifdef _SCALE_SKIA_
684
            size_t rowBytes = info.minRowBytes();
685
            size_t size = info.computeByteSize(rowBytes);
686
            SkImageInfo info = target.info();
687
 
688
            if (gPageManager && gPageManager->getScaleFactor() != 1.0)
689
            {
690
                SkPaint paint;
691
                int left, top;
692
 
693
                paint.setBlendMode(SkBlendMode::kSrc);
694
                paint.setFilterQuality(kHigh_SkFilterQuality);
695
                // Calculate new dimension
696
                double scaleFactor = gPageManager->getScaleFactor();
697
                MSG_DEBUG("Using scale factor " << scaleFactor);
698
                int lwidth = (int)((double)info.width() * scaleFactor);
699
                int lheight = (int)((double)info.height() * scaleFactor);
700
                int twidth = (int)((double)mSubpage.width * scaleFactor);
701
                int theight = (int)((double)mSubpage.height * scaleFactor);
702
                calcPosition(lwidth, lheight, &left, &top);
703
                // Create a canvas and draw new image
704
                sk_sp<SkImage> im = SkImage::MakeFromBitmap(target);
705
 
706
                if (!allocPixels(twidth, theight, &target))
707
                    return;
708
 
709
                target.eraseColor(TColor::getSkiaColor(mSubpage.sr[0].cf));
710
                SkCanvas can(target, SkSurfaceProps());
711
                SkRect rect = SkRect::MakeXYWH(left, top, lwidth, lheight);
712
                can.drawImageRect(im, rect, &paint);
713
                MSG_DEBUG("Scaled size of background image: " << left << ", " << top << ", " << lwidth << ", " << lheight);
714
            }
715
#endif
716
        }
717
    }
718
 
719
    if (mSubpage.sr.size() > 0 && !mSubpage.sr[0].te.empty())
720
    {
721
        MSG_DEBUG("Drawing a text only on background image ...");
722
 
723
        if (drawText(mSubpage, &target))
724
            haveImage = true;
725
    }
726
 
727
    // Check for a frame and draw it if there is one.
728
    if (!mSubpage.sr[0].bs.empty())
729
    {
730
        if (!drawBorder(&target, mSubpage.sr[0].bs, mSubpage.width, mSubpage.height, mSubpage.sr[0].cb))
731
        {
732
            if (drawFrame(mSubpage, &target))
733
                haveImage = true;
734
        }
735
    }
736
 
737
    if (haveImage)
738
    {
739
        if (mSubpage.sr[0].oo < 255)
740
            setOpacity(&target, mSubpage.sr[0].oo);
741
 
742
        mBgImage = target;
743
    }
744
 
745
    return mBgImage;
746
}
747
 
748
void TSubPage::drop()
749
{
750
    DECL_TRACER("TSubPage::drop()");
751
 
752
    stopTimer();
753
 
754
    if (mVisible && _callDropSubPage)
755
        _callDropSubPage((mSubpage.pageID << 16) & 0xffff0000, mParent);
756
#if TESTMODE == 1
757
    else
758
    {
759
        __success = true;
760
        setScreenDone();
761
    }
762
#endif
763
 
764
    // Set all elements of subpage invisible
765
    BUTTONS_T *bt = TPageInterface::getButtons();
766
 
767
    while (bt)
768
    {
769
        bt->button->hide();
770
        bt = bt->next;
771
    }
772
 
773
    mZOrder = -1;
774
    mVisible = false;
775
}
776
 
777
void TSubPage::startTimer()
778
{
779
    DECL_TRACER("TSubPage::startTimer()");
780
 
781
    if (mSubpage.timeout <= 0 || mTimerRunning)
782
        return;
783
 
784
    try
785
    {
786
        mThreadTimer = std::thread([=] { runTimer(); });
787
        mThreadTimer.detach();
788
    }
789
    catch (std::exception& e)
790
    {
791
        MSG_ERROR("Error starting a timeout thread: " << e.what());
792
        return;
793
    }
794
}
795
 
796
void TSubPage::runTimer()
797
{
798
    DECL_TRACER("TSubPage::runTimer()");
799
 
800
    if (mTimerRunning)
801
        return;
802
 
803
    mTimerRunning = true;
804
    ulong tm = mSubpage.timeout * 100;
805
    ulong unit = 100;
806
    ulong total = 0;
807
 
808
    while (mTimerRunning && !prg_stopped && total < tm)
809
    {
810
        std::this_thread::sleep_for(std::chrono::milliseconds(unit));
811
        total += unit;
812
    }
813
 
814
    drop();
815
    mTimerRunning = false;
816
}
817
 
818
#ifdef _SCALE_SKIA_
819
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top, bool scale)
820
#else
821
void TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)
822
#endif
823
{
824
    DECL_TRACER("TSubPage::calcPosition(int im_width, int im_height, int *left, int *top)");
825
 
826
    int nw = mSubpage.width;
827
    int nh = mSubpage.height;
828
#ifdef _SCALE_SKIA_
829
    if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
830
    {
831
        nw = (int)((double)mSubpage.width * gPageManager->getScaleFactor());
832
        nh = (int)((double)mSubpage.height * gPageManager->getScaleFactor());
833
    }
834
#endif
835
    switch (mSubpage.sr[0].jb)
836
    {
837
        case 0: // absolute position
838
            *left = mSubpage.sr[0].bx;
839
            *top = mSubpage.sr[0].by;
840
#ifdef _SCALE_SKIA_
841
            if (scale && gPageManager && gPageManager->getScaleFactor() != 1.0)
842
            {
843
                *left = (int)((double)mSubpage.sr[0].bx * gPageManager->getScaleFactor());
844
                *left = (int)((double)mSubpage.sr[0].by * gPageManager->getScaleFactor());
845
            }
846
#endif
847
        break;
848
 
849
        case 1: // top, left
850
            *left = 0;
851
            *top = 0;
852
        break;
853
 
854
        case 2: // center, top
855
            *left = (nw - im_width) / 2;
856
            *top = 0;
857
        break;
858
 
859
        case 3: // right, top
860
            *left = nw - im_width;
861
            *top = 0;
862
        break;
863
 
864
        case 4: // left, middle
865
            *left = 0;
866
            *top = (nh - im_height) / 2;
867
        break;
868
 
869
        case 6: // right, middle
870
            *left = nw - im_width;
871
            *top = (nh - im_height) / 2;
872
        break;
873
 
874
        case 7: // left, bottom
875
            *left = 0;
876
            *top = nh - im_height;
877
        break;
878
 
879
        case 8: // center, bottom
880
            *left = (nw - im_width) / 2;
881
            *top = nh - im_height;
882
        break;
883
 
884
        case 9: // right, bottom
885
            *left = nw - im_width;
886
            *top = nh - im_height;
887
        break;
888
 
889
        default:    // center middle
890
            *left = (nw - im_width) / 2;
891
            *top = (nh - im_height) / 2;
892
    }
893
 
894
    if (*left < 0)
895
        *left = 0;
896
 
897
    if (*top < 0)
898
        *top = 0;
899
}
900
 
901
RECT_T TSubPage::getRegion()
902
{
903
    DECL_TRACER("TSubPage::getRegion()");
904
    return {mSubpage.left, mSubpage.top, mSubpage.width, mSubpage.height};
905
}
906
 
907
/**
908
 * This method is called indirectly from the GUI after a mouse click. If This
909
 * subpage matches the clicked coordinates, than the elements are tested. If
910
 * an element is found that matches the coordinates it gets the click. It
911
 * depends on the kind of element what happens.
912
 */
913
void TSubPage::doClick(int x, int y, bool pressed)
914
{
915
    DECL_TRACER("TSubPage::doClick(int x, int y)");
916
 
917
    BUTTONS_T *button = TPageInterface::getButtons();
918
 
919
    if (!button)
920
        return;
921
 
922
    // Find last button
923
    while (button && button->next)
924
        button = button->next;
925
 
926
    // Scan in reverse order
927
    while (button)
928
    {
929
        TButton *but = button->button;
930
        bool clickable = but->isClickable();
931
        MSG_DEBUG("Testing button " << but->getButtonIndex() << " (" << but->getButtonName() << "): " << (clickable ? "CLICKABLE" : "NOT CLICKABLE"));
932
 
933
        if (clickable && x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
934
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
935
        {
936
            MSG_DEBUG("Clicking button " << but->getButtonIndex() << ": " << but->getButtonName() << " to state " << (pressed ? "PRESS" : "RELEASE"));
937
            int btX = x - but->getLeftPosition();
938
            int btY = y - but->getTopPosition();
939
 
940
            if (but->doClick(btX, btY, pressed))
941
                break;
942
        }
943
 
944
        button = button->previous;
945
    }
946
}
947
 
948
void TSubPage::moveMouse(int x, int y)
949
{
950
    DECL_TRACER("TSubPage::moveMouse(int x, int y)");
951
 
952
    BUTTONS_T *button = TPageInterface::getButtons();
953
 
954
    if (!button)
955
        return;
956
 
957
    // Find last button
958
    while (button && button->next)
959
        button = button->next;
960
 
961
    // Scan in reverse order
962
    while (button)
963
    {
964
        TButton *but = button->button;
965
 
966
        if (but->getButtonType() != BARGRAPH && but->getButtonType() != JOYSTICK)
967
        {
968
            button = button->previous;
969
            continue;
970
        }
971
 
972
        bool clickable = but->isClickable();
973
 
974
        if (clickable && x > but->getLeftPosition() && x < (but->getLeftPosition() + but->getWidth()) &&
975
            y > but->getTopPosition() && y < (but->getTopPosition() + but->getHeight()))
976
        {
977
            int btX = x - but->getLeftPosition();
978
            int btY = y - but->getTopPosition();
979
 
980
            if (but->getButtonType() == BARGRAPH)
981
                but->moveBargraphLevel(btX, btY);
982
            else if (but->getButtonType() == JOYSTICK && !but->getLevelFuction().empty())
983
            {
984
                but->drawJoystick(btX, btY);
985
                but->sendJoystickLevels();
986
            }
987
 
988
            break;
989
        }
990
 
991
        button = button->previous;
992
    }
993
}