Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
202 andreas 1
/*
258 andreas 2
 * Copyright (C) 2022, 2023 by Andreas Theofilu <andreas@theosys.at>
202 andreas 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 <string>
20
 
203 andreas 21
#include <include/core/SkFont.h>
22
#include <include/core/SkFontMetrics.h>
23
#include <include/core/SkTextBlob.h>
258 andreas 24
#include <include/core/SkRegion.h>
25
#include <include/core/SkImageFilter.h>
26
#include <include/effects/SkImageFilters.h>
203 andreas 27
 
202 andreas 28
#include "tpageinterface.h"
205 andreas 29
#include "tsystemsound.h"
206 andreas 30
#include "ttpinit.h"
204 andreas 31
#include "tconfig.h"
203 andreas 32
#include "tresources.h"
206 andreas 33
#include "tpagemanager.h"
317 andreas 34
#include "tintborder.h"
203 andreas 35
#include "terror.h"
202 andreas 36
 
404 andreas 37
#if __cplusplus < 201402L
38
#   error "This module requires at least C++14 standard!"
39
#else
40
#   if __cplusplus < 201703L
41
#       include <experimental/filesystem>
42
namespace fs = std::experimental::filesystem;
43
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
44
#   else
45
#       include <filesystem>
46
#       ifdef __ANDROID__
47
namespace fs = std::__fs::filesystem;
48
#       else
49
namespace fs = std::filesystem;
50
#       endif
51
#   endif
52
#endif
53
 
202 andreas 54
using std::string;
55
using std::vector;
56
 
57
bool TPageInterface::drawText(PAGE_T& pinfo, SkBitmap *img)
58
{
59
    MSG_TRACE("TPageInterface::drawText(PAGE_T& pinfo, SkImage& img)");
60
 
384 andreas 61
    if (pinfo.sr.empty() || pinfo.sr[0].te.empty())
202 andreas 62
        return true;
63
 
64
    MSG_DEBUG("Searching for font number " << pinfo.sr[0].fi << " with text " << pinfo.sr[0].te);
65
    FONT_T font = mFonts->getFont(pinfo.sr[0].fi);
66
 
258 andreas 67
    if (font.file.empty())
202 andreas 68
    {
258 andreas 69
        MSG_WARNING("No font file name found for font " << pinfo.sr[0].fi);
70
        return false;
71
    }
202 andreas 72
 
258 andreas 73
    SkCanvas canvas(*img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
74
    sk_sp<SkTypeface> typeFace = mFonts->getTypeFace(pinfo.sr[0].fi);
202 andreas 75
 
258 andreas 76
    if (!typeFace)
77
    {
78
        MSG_ERROR("Error creating type face " << font.fullName);
79
        TError::setError();
80
        return false;
81
    }
202 andreas 82
 
258 andreas 83
    SkScalar fontSizePt = ((SkScalar)font.size * 1.322);    // Calculate points from pixels (close up)
84
    SkFont skFont(typeFace, fontSizePt);                    // Skia require the font size in points
202 andreas 85
 
258 andreas 86
    SkPaint paint;
87
    paint.setAntiAlias(true);
88
    SkColor color = TColor::getSkiaColor(pinfo.sr[0].ct);
89
    paint.setColor(color);
90
    paint.setStyle(SkPaint::kFill_Style);
202 andreas 91
 
258 andreas 92
    SkFontMetrics metrics;
93
    skFont.getMetrics(&metrics);
94
    int lines = numberLines(pinfo.sr[0].te);
202 andreas 95
 
258 andreas 96
    if (lines > 1 || pinfo.sr[0].ww)
97
    {
98
        vector<string> textLines;
202 andreas 99
 
258 andreas 100
        if (!pinfo.sr[0].ww)
101
            textLines = splitLine(pinfo.sr[0].te);
102
        else
103
        {
104
            textLines = splitLine(pinfo.sr[0].te, pinfo.width, pinfo.height, skFont, paint);
300 andreas 105
            lines = (int)textLines.size();
258 andreas 106
        }
202 andreas 107
 
258 andreas 108
        MSG_DEBUG("Calculated number of lines: " << textLines.size());
109
        int lineHeight = calcLineHeight(pinfo.sr[0].te, skFont);
110
        int totalHeight = lineHeight * lines;
202 andreas 111
 
258 andreas 112
        if (totalHeight > pinfo.height)
113
        {
114
            lines = pinfo.height / lineHeight;
115
            totalHeight = lineHeight * lines;
116
        }
202 andreas 117
 
258 andreas 118
        MSG_DEBUG("Line height: " << lineHeight << ", total height: " << totalHeight);
119
        Button::POSITION_t position = calcImagePosition(&pinfo, pinfo.width, totalHeight, Button::SC_TEXT, 0);
120
        MSG_DEBUG("Position frame: l: " << position.left << ", t: " << position.top << ", w: " << position.width << ", h: " << position.height);
202 andreas 121
 
258 andreas 122
        if (!position.valid)
123
        {
124
            MSG_ERROR("Error calculating the text position!");
125
            TError::setError();
126
            return false;
127
        }
202 andreas 128
 
258 andreas 129
        vector<string>::iterator iter;
130
        int line = 0;
202 andreas 131
 
258 andreas 132
        for (iter = textLines.begin(); iter != textLines.end(); iter++)
202 andreas 133
        {
258 andreas 134
            sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(iter->c_str(), skFont);
202 andreas 135
            SkRect rect;
258 andreas 136
            skFont.measureText(iter->c_str(), iter->length(), SkTextEncoding::kUTF8, &rect, &paint);
137
            Button::POSITION_t pos = calcImagePosition(&pinfo, rect.width(), lineHeight, Button::SC_TEXT, 1);
202 andreas 138
 
258 andreas 139
            if (!pos.valid)
202 andreas 140
            {
141
                MSG_ERROR("Error calculating the text position!");
142
                TError::setError();
143
                return false;
144
            }
258 andreas 145
            MSG_DEBUG("Triing to print line: " << *iter);
202 andreas 146
 
258 andreas 147
            SkScalar startX = (SkScalar)pos.left;
148
            SkScalar startY = (SkScalar)position.top + lineHeight * line;
149
            MSG_DEBUG("x=" << startX << ", y=" << startY);
150
            canvas.drawTextBlob(blob, startX, startY + lineHeight / 2 + 4, paint);
151
            line++;
152
 
153
            if (line > lines)
154
                break;
202 andreas 155
        }
156
    }
258 andreas 157
    else    // single line
202 andreas 158
    {
258 andreas 159
        sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(pinfo.sr[0].te.c_str(), skFont);
160
        SkRect rect;
161
        skFont.measureText(pinfo.sr[0].te.c_str(), pinfo.sr[0].te.length(), SkTextEncoding::kUTF8, &rect, &paint);
162
        Button::POSITION_t position = calcImagePosition(&pinfo, rect.width(), (rect.height() * (float)lines), Button::SC_TEXT, 0);
163
 
164
        if (!position.valid)
165
        {
166
            MSG_ERROR("Error calculating the text position!");
167
            TError::setError();
168
            return false;
169
        }
170
 
171
        MSG_DEBUG("Printing line " << pinfo.sr[0].te);
172
        SkScalar startX = (SkScalar)position.left;
173
        SkScalar startY = (SkScalar)position.top + metrics.fCapHeight; // + metrics.fLeading; // (metrics.fAscent * -1.0);
174
        canvas.drawTextBlob(blob, startX, startY, paint);
202 andreas 175
    }
176
 
177
    return true;
178
}
179
 
258 andreas 180
bool TPageInterface::drawFrame(PAGE_T& pinfo, SkBitmap* bm)
181
{
182
    DECL_TRACER("TPageInterface::drawFrame(PAGE_T& pinfo, SkBitmap* bm)");
183
 
184
    int instance = 0;
185
 
186
    if (pinfo.sr[instance].bs.empty())
187
    {
188
        MSG_DEBUG("No border defined.");
189
        return false;
190
    }
191
 
317 andreas 192
    // First we look into our internal border table
193
    Border::TIntBorder *intBorder = new Border::TIntBorder;
194
 
195
    if (intBorder && intBorder->drawBorder(bm, pinfo.sr[instance].bs, pinfo.width, pinfo.height, pinfo.sr[instance].cb))
196
    {
197
        delete intBorder;
198
        return true;
199
    }
200
 
201
    if (intBorder)
202
    {
203
        delete intBorder;
204
        intBorder = nullptr;
205
    }
206
 
258 andreas 207
    // Try to find the border in the system table
404 andreas 208
    BORDER_t bd;
258 andreas 209
    bool classExist = (gPageManager && gPageManager->getSystemDraw());
210
 
211
    if (!classExist)
212
        return false;
213
 
214
    string borderName = pinfo.sr[0].bs;
215
 
216
    if (!gPageManager->getSystemDraw()->getBorder(borderName, TSystemDraw::LT_OFF, &bd, borderName))
217
        return false;
218
 
219
    MSG_DEBUG("System border \"" << borderName << "\" found.");
220
    SkColor color = TColor::getSkiaColor(pinfo.sr[instance].cb);      // border color
221
    MSG_DEBUG("Button color: #" << std::setw(6) << std::setfill('0') << std::hex << color << std::dec);
222
    // Load images
223
    SkBitmap imgB, imgBR, imgR, imgTR, imgT, imgTL, imgL, imgBL;
224
 
404 andreas 225
    if (!getBorderFragment(bd.b, bd.b_alpha, &imgB, color))
258 andreas 226
        return false;
227
 
404 andreas 228
    MSG_DEBUG("Got images " << bd.b << " and " << bd.b_alpha << " with size " << imgB.info().width() << " x " << imgB.info().height());
229
    if (!getBorderFragment(bd.br, bd.br_alpha, &imgBR, color))
258 andreas 230
        return false;
231
 
404 andreas 232
    MSG_DEBUG("Got images " << bd.br << " and " << bd.br_alpha << " with size " << imgBR.info().width() << " x " << imgBR.info().height());
233
    if (!getBorderFragment(bd.r, bd.r_alpha, &imgR, color))
258 andreas 234
        return false;
235
 
404 andreas 236
    MSG_DEBUG("Got images " << bd.r << " and " << bd.r_alpha << " with size " << imgR.info().width() << " x " << imgR.info().height());
237
    if (!getBorderFragment(bd.tr, bd.tr_alpha, &imgTR, color))
258 andreas 238
        return false;
239
 
404 andreas 240
    MSG_DEBUG("Got images " << bd.tr << " and " << bd.tr_alpha << " with size " << imgTR.info().width() << " x " << imgTR.info().height());
241
    if (getBorderFragment(bd.t, bd.t_alpha, &imgT, color))
258 andreas 242
        return false;
243
 
404 andreas 244
    MSG_DEBUG("Got images " << bd.t << " and " << bd.t_alpha << " with size " << imgT.info().width() << " x " << imgT.info().height());
245
    if (!getBorderFragment(bd.tl, bd.tl_alpha, &imgTL, color))
258 andreas 246
        return false;
247
 
404 andreas 248
    MSG_DEBUG("Got images " << bd.tl << " and " << bd.tl_alpha << " with size " << imgTL.info().width() << " x " << imgTL.info().height());
249
    if (!getBorderFragment(bd.l, bd.l_alpha, &imgL, color))
258 andreas 250
        return false;
251
 
404 andreas 252
    MSG_DEBUG("Got images " << bd.l << " and " << bd.l_alpha << " with size " << imgL.info().width() << " x " << imgL.info().height());
253
    if (!getBorderFragment(bd.bl, bd.bl_alpha, &imgBL, color))
258 andreas 254
        return false;
255
 
404 andreas 256
    MSG_DEBUG("Got images " << bd.bl << " and " << bd.bl_alpha << " with size " << imgBL.info().width() << " x " << imgBL.info().height());
258 andreas 257
    MSG_DEBUG("Button image size: " << (imgTL.info().width() + imgT.info().width() + imgTR.info().width()) << " x " << (imgTL.info().height() + imgL.info().height() + imgBL.info().height()));
258
    MSG_DEBUG("Total size: " << pinfo.width << " x " << pinfo.height);
259
    stretchImageWidth(&imgB, pinfo.width - imgBL.info().width() - imgBR.info().width());
260
    stretchImageWidth(&imgT, pinfo.width - imgTL.info().width() - imgTR.info().width());
261
    stretchImageHeight(&imgL, pinfo.height - imgTL.info().height() - imgBL.info().height());
262
    stretchImageHeight(&imgR, pinfo.height - imgTR.info().height() - imgBR.info().height());
263
    MSG_DEBUG("Stretched button image size: " << (imgTL.info().width() + imgT.info().width() + imgTR.info().width()) << " x " << (imgTL.info().height() + imgL.info().height() + imgBL.info().height()));
264
    // Draw the frame
404 andreas 265
    SkBitmap frame;
266
    allocPixels(bm->info().width(), bm->info().height(), &frame);
267
    frame.eraseColor(SK_ColorTRANSPARENT);
268
    SkCanvas target(*bm, SkSurfaceProps());
269
    SkCanvas canvas(frame, SkSurfaceProps());
258 andreas 270
    SkPaint paint;
271
 
404 andreas 272
    paint.setBlendMode(SkBlendMode::kSrcOver);
273
    paint.setAntiAlias(true);
274
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgB);   // bottom
258 andreas 275
    canvas.drawImage(_image, imgBL.info().width(), pinfo.height - imgB.info().height(), SkSamplingOptions(), &paint);
404 andreas 276
    _image = SkImages::RasterFromBitmap(imgT);                  // top
277
    canvas.drawImage(_image, imgTL.info().width(), 0, SkSamplingOptions(), &paint);
278
    _image = SkImages::RasterFromBitmap(imgBR);                 // bottom right
258 andreas 279
    canvas.drawImage(_image, pinfo.width - imgBR.info().width(), pinfo.height - imgBR.info().height(), SkSamplingOptions(), &paint);
404 andreas 280
    _image = SkImages::RasterFromBitmap(imgTR);                 // top right
258 andreas 281
    canvas.drawImage(_image, pinfo.width - imgTR.info().width(), 0, SkSamplingOptions(), &paint);
404 andreas 282
    _image = SkImages::RasterFromBitmap(imgTL);                 // top left
258 andreas 283
    canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
404 andreas 284
    _image = SkImages::RasterFromBitmap(imgBL);                 // bottom left
285
    canvas.drawImage(_image, 0, pinfo.height - imgBL.info().height(), SkSamplingOptions(), &paint);
286
    _image = SkImages::RasterFromBitmap(imgL);                  // left
258 andreas 287
    canvas.drawImage(_image, 0, imgTL.info().height(), SkSamplingOptions(), &paint);
404 andreas 288
    _image = SkImages::RasterFromBitmap(imgR);                  // right
289
    canvas.drawImage(_image, pinfo.width - imgR.info().width(), imgTR.info().height(), SkSamplingOptions(), &paint);
290
 
291
    Border::TIntBorder iborder;
292
    iborder.erasePart(bm, frame, Border::ERASE_OUTSIDE, imgL.info().width());
293
    _image = SkImages::RasterFromBitmap(frame);
294
    paint.setBlendMode(SkBlendMode::kSrcATop);
295
    target.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
258 andreas 296
    return true;
297
}
298
 
203 andreas 299
Button::POSITION_t TPageInterface::calcImagePosition(PAGE_T *page, int width, int height, Button::CENTER_CODE cc, int line)
202 andreas 300
{
203 andreas 301
    DECL_TRACER("TPageInterface::calcImagePosition(PAGE_T *page, int with, int height, CENTER_CODE code, int number)");
202 andreas 302
 
258 andreas 303
    if (!page)
304
        return Button::POSITION_t();
305
 
203 andreas 306
    Button::SR_T act_sr;
307
    Button::POSITION_t position;
202 andreas 308
    int ix, iy;
309
 
258 andreas 310
    if (page->sr.size() == 0)
311
    {
312
        if (sr.size() == 0)
313
            return position;
202 andreas 314
 
258 andreas 315
        act_sr = sr.at(0);
316
    }
317
    else
318
        act_sr = page->sr.at(0);
319
 
202 andreas 320
    //    int border_size = getBorderSize(act_sr.bs);
321
    int border_size = 0;
322
    int code, border = border_size;
323
    string dbgCC;
324
    int rwt = 0, rht = 0;
325
 
326
    switch (cc)
327
    {
203 andreas 328
        case Button::SC_ICON:
202 andreas 329
            code = act_sr.ji;
330
            ix = act_sr.ix;
331
            iy = act_sr.iy;
332
            border = border_size = 0;
333
            dbgCC = "ICON";
334
            rwt = width;
335
            rht = height;
336
            break;
337
 
203 andreas 338
        case Button::SC_BITMAP:
202 andreas 339
            code = act_sr.jb;
340
            ix = act_sr.bx;
341
            iy = act_sr.by;
342
            dbgCC = "BITMAP";
203 andreas 343
            rwt = std::min(page->width - border * 2, width);
344
            rht = std::min(page->height - border_size * 2, height);
202 andreas 345
            break;
346
 
203 andreas 347
        case Button::SC_TEXT:
202 andreas 348
            code = act_sr.jt;
349
            ix = act_sr.tx;
350
            iy = act_sr.ty;
351
            dbgCC = "TEXT";
352
            border += 4;
203 andreas 353
            rwt = std::min(page->width - border * 2, width);
354
            rht = std::min(page->height - border_size * 2, height);
202 andreas 355
            break;
356
    }
357
 
358
    if (width > rwt || height > rht)
359
        position.overflow = true;
360
 
361
    switch (code)
362
    {
363
        case 0: // absolute position
364
            position.left = ix;
365
 
258 andreas 366
            if (cc == Button::SC_TEXT && line > 0)
202 andreas 367
                position.top = iy + height * line;
258 andreas 368
            else
369
                position.top = iy;
202 andreas 370
 
258 andreas 371
            if (cc == Button::SC_BITMAP && ix < 0 && rwt < width)
372
                position.left *= -1;
202 andreas 373
 
258 andreas 374
            if (cc == Button::SC_BITMAP && iy < 0 && rht < height)
375
                position.top += -1;
202 andreas 376
 
258 andreas 377
            position.width = rwt;
378
            position.height = rht;
202 andreas 379
        break;
380
 
381
        case 1: // top, left
203 andreas 382
            if (cc == Button::SC_TEXT)
202 andreas 383
            {
384
                position.left = border;
258 andreas 385
 
386
                if (line > 0)
387
                    position.top = height * line;
388
                else
389
                    position.top = border;
202 andreas 390
            }
391
 
392
            position.width = rwt;
393
            position.height = rht;
258 andreas 394
        break;
202 andreas 395
 
396
        case 2: // center, top
203 andreas 397
            if (cc == Button::SC_TEXT)
258 andreas 398
            {
399
                if (line > 0)
400
                    position.top = height * line;
401
                else
402
                    position.top = border;
403
            }
202 andreas 404
 
258 andreas 405
            position.left = (page->width - rwt) / 2;
406
            position.height = rht;
407
            position.width = rwt;
202 andreas 408
        break;
409
 
410
        case 3: // right, top
203 andreas 411
            position.left = page->width - rwt;
202 andreas 412
 
203 andreas 413
            if (cc == Button::SC_TEXT)
202 andreas 414
            {
415
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
258 andreas 416
 
417
                if (line > 0)
418
                    position.top = height * line;
419
                else
420
                    position.top = border;
202 andreas 421
            }
422
 
423
            position.width = rwt;
424
            position.height = rht;
258 andreas 425
        break;
202 andreas 426
 
427
        case 4: // left, middle
203 andreas 428
            if (cc == Button::SC_TEXT)
202 andreas 429
            {
430
                position.left = border;
258 andreas 431
 
432
                if (line > 0)
433
                    position.top = ((page->height - rht) / 2) + (height / 2 * line);
434
                else
435
                    position.top = (page->height - rht) / 2;
202 andreas 436
            }
437
            else
203 andreas 438
                position.top = (page->height - rht) / 2;
202 andreas 439
 
258 andreas 440
            position.width = rwt;
441
            position.height = rht;
202 andreas 442
        break;
443
 
444
        case 6: // right, middle
203 andreas 445
            position.left = page->width - rwt;
202 andreas 446
 
203 andreas 447
            if (cc == Button::SC_TEXT)
202 andreas 448
            {
449
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
258 andreas 450
 
451
                if (line > 0)
452
                    position.top = ((page->height - rht) / 2) + (height / 2 * line);
453
                else
454
                    position.top = (page->height - rht) / 2;
202 andreas 455
            }
456
            else
203 andreas 457
                position.top = (page->height - rht) / 2;
202 andreas 458
 
258 andreas 459
            position.width = rwt;
460
            position.height = rht;
202 andreas 461
        break;
462
 
463
        case 7: // left, bottom
203 andreas 464
            if (cc == Button::SC_TEXT)
202 andreas 465
            {
466
                position.left = border_size;
258 andreas 467
 
468
                if (line > 0)
469
                    position.top = (page->height - rht) - height * line;
470
                else
471
                    position.top = page->height - rht;
202 andreas 472
            }
473
            else
203 andreas 474
                position.top = page->height - rht;
202 andreas 475
 
258 andreas 476
            position.width = rwt;
477
            position.height = rht;
202 andreas 478
        break;
479
 
480
        case 8: // center, bottom
203 andreas 481
            position.left = (page->width - rwt) / 2;
202 andreas 482
 
203 andreas 483
            if (cc == Button::SC_TEXT)
258 andreas 484
            {
485
                if (line > 0)
486
                    position.top = (page->height - rht) - height * line;
487
                else
488
                    position.top = page->height - rht;
489
            }
490
            else
491
                position.top = page->height - rht;
202 andreas 492
 
258 andreas 493
            position.width = rwt;
494
            position.height = rht;
202 andreas 495
        break;
496
 
497
        case 9: // right, bottom
203 andreas 498
            position.left = page->width - rwt;
202 andreas 499
 
203 andreas 500
            if (cc == Button::SC_TEXT)
202 andreas 501
            {
502
                position.left = (((position.left - border) < 0) ? 0 : position.left - border);
258 andreas 503
 
504
                if (line > 0)
505
                    position.top = (page->height - rht) - height * line;
506
                else
507
                    position.top = page->height - rht;
202 andreas 508
            }
509
            else
203 andreas 510
                position.top = page->height - rht;
202 andreas 511
        break;
512
 
513
        default: // center, middle
203 andreas 514
            position.left = (page->width - rwt) / 2;
202 andreas 515
 
203 andreas 516
            if (cc == Button::SC_TEXT)
258 andreas 517
            {
518
                if (line > 0)
519
                    position.top = ((page->height - rht) / 2) + (height / 2 * line);
520
                else
521
                    position.top = (page->height - rht) / 2;
522
            }
523
            else
524
                position.top = (page->height - rht) / 2;
202 andreas 525
 
258 andreas 526
            position.width = rwt;
527
            position.height = rht;
202 andreas 528
    }
529
 
530
    MSG_DEBUG("Type: " << dbgCC << ", PosType=" << code << ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width << ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
531
    position.valid = true;
532
    return position;
533
}
534
 
535
int TPageInterface::calcLineHeight(const string& text, SkFont& font)
536
{
537
    DECL_TRACER("TPageInterface::calcLineHeight(const string& text, SkFont& font)");
538
 
539
    sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString(text.c_str(), font);
540
    SkRect rect = blob.get()->bounds();
541
    return rect.height();
542
}
543
 
544
int TPageInterface::numberLines(const string& str)
545
{
546
    DECL_TRACER("TPageInterface::numberLines(const string& str)");
547
 
548
    int lines = 1;
549
 
550
    for (size_t i = 0; i < str.length(); i++)
551
    {
552
        if (str.at(i) == '\n')
553
            lines++;
554
    }
555
 
258 andreas 556
    MSG_DEBUG("Detected " << lines << " lines.");
202 andreas 557
    return lines;
558
}
203 andreas 559
 
560
Button::BUTTONS_T *TPageInterface::addButton(Button::TButton* button)
561
{
562
    DECL_TRACER("*TPageInterface::addButton(TButton* button)");
563
 
564
    if (!button)
565
    {
566
        MSG_ERROR("Parameter is NULL!");
567
        TError::setError();
568
        return nullptr;
569
    }
570
 
266 andreas 571
    // We try to add this button to the list of system buttons which will
572
    // succeed only if it is one of the supported system buttons.
573
    addSysButton(button);
271 andreas 574
 
203 andreas 575
    try
576
    {
577
        Button::BUTTONS_T *chain = new Button::BUTTONS_T;
578
        chain->button = button;
579
        chain->next = nullptr;
580
        chain->previous = nullptr;
581
        Button::BUTTONS_T *bts = mButtons;
582
 
583
        if (bts)
584
        {
585
            Button::BUTTONS_T *p = bts;
586
 
587
            while (p && p->next)
588
                p = p->next;
589
 
590
            p->next = chain;
591
            chain->previous = p;
592
        }
593
        else
594
            mButtons = chain;
595
 
596
        return chain;
597
    }
598
    catch (std::exception& e)
599
    {
600
        MSG_ERROR("Memory error: " << e.what());
601
        TError::setError();
602
    }
603
 
604
    return nullptr;
605
}
606
 
607
bool TPageInterface::hasButton(int id)
608
{
609
    DECL_TRACER("TPageInterface::hasButton(int id)");
610
 
611
    Button::BUTTONS_T *bt = mButtons;
612
 
613
    while (bt)
614
    {
615
        if (bt->button && bt->button->getButtonIndex() == id)
616
            return true;
617
 
618
        bt = bt->next;
619
    }
620
 
621
    return false;
622
}
623
 
624
Button::TButton *TPageInterface::getButton(int id)
625
{
626
    DECL_TRACER("TPageInterface::getButton(int id)");
627
 
628
    Button::BUTTONS_T *bt = mButtons;
629
 
630
    while (bt)
631
    {
632
        if (bt->button && bt->button->getButtonIndex() == id)
633
            return bt->button;
634
 
635
        bt = bt->next;
636
    }
637
 
638
    return nullptr;
639
}
640
 
641
vector<Button::TButton *> TPageInterface::getButtons(int ap, int ad)
642
{
643
    DECL_TRACER("TPageInterface::getButtons(int ap, int ad)");
644
 
645
    vector<Button::TButton *> list;
646
    Button::BUTTONS_T *bt = mButtons;
647
 
648
    while (bt)
649
    {
650
        if (bt->button->getAddressPort() == ap && bt->button->getAddressChannel() == ad)
651
            list.push_back(bt->button);
652
 
653
        bt = bt->next;
654
    }
655
 
656
    return list;
657
}
658
 
659
vector<Button::TButton *> TPageInterface::getAllButtons()
660
{
661
    DECL_TRACER("TPageInterface::getAllButtons()");
662
 
663
    vector<Button::TButton *> list;
664
    Button::BUTTONS_T *bt = mButtons;
665
 
666
    while(bt)
667
    {
668
        list.push_back(bt->button);
669
        bt = bt->next;
670
    }
671
 
672
    return list;
673
}
674
 
675
Button::TButton *TPageInterface::getFirstButton()
676
{
677
    DECL_TRACER("TPageInterface::getFirstButton()");
678
 
679
    mLastButton = 0;
680
 
681
    if (mButtons)
682
        return mButtons->button;
683
 
684
    return nullptr;
685
}
686
 
687
Button::TButton *TPageInterface::getNextButton()
688
{
689
    DECL_TRACER("TPageInterface::getNextButton()");
690
 
691
    Button::BUTTONS_T *but = mButtons;
692
    int count = 0;
693
    mLastButton++;
694
 
695
    while (but)
696
    {
697
        if (but->button && count == mLastButton)
698
            return but->button;
699
 
700
        but = but->next;
701
        count++;
702
    }
703
 
704
    return nullptr;
705
}
706
 
707
Button::TButton *TPageInterface::getLastButton()
708
{
709
    DECL_TRACER("TPageInterface::getLastButton()");
710
 
711
    Button::BUTTONS_T *but = mButtons;
712
    mLastButton = 0;
713
 
714
    while (but && but->next)
715
    {
716
        mLastButton++;
717
        but = but->next;
718
    }
719
 
720
    if (!but)
721
        return nullptr;
722
 
723
    return but->button;
724
}
725
 
726
Button::TButton *TPageInterface::getPreviousButton()
727
{
728
    DECL_TRACER("TPageInterface::getPreviousButton()");
729
 
730
    Button::BUTTONS_T *but = mButtons;
731
    int count = 0;
732
 
733
    if (mLastButton)
734
        mLastButton--;
735
    else
736
        return nullptr;
737
 
738
    while (but)
739
    {
740
        if (but->button && count == mLastButton)
741
            return but->button;
742
 
743
        but = but->next;
744
        count++;
745
    }
746
 
747
    return nullptr;
748
}
749
 
750
/*
751
 * Sort the button according to their Z-order.
752
 * The button with the highest Z-order will be the last button in the chain.
753
 * The algorithm is a bubble sort algorithm.
754
 */
755
bool TPageInterface::sortButtons()
756
{
757
    DECL_TRACER("TPageInterface::sortButtons()");
758
 
759
    bool turned = true;
760
 
761
    while (turned)
762
    {
763
        Button::BUTTONS_T *button = mButtons;
764
        turned = false;
765
 
766
        while (button)
767
        {
768
            int zo = button->button->getZOrder();
769
 
770
            if (button->previous)
771
            {
772
                if (zo < button->previous->button->getZOrder())
773
                {
774
                    Button::BUTTONS_T *pprev = button->previous->previous;
775
                    Button::BUTTONS_T *prev = button->previous;
776
                    Button::BUTTONS_T *next = button->next;
777
 
778
                    if (pprev)
779
                        pprev->next = button;
780
 
781
                    prev->next = next;
782
                    prev->previous = button;
783
                    button->next = prev;
784
                    button->previous = pprev;
785
 
786
                    if (!pprev)
787
                        setButtons(button);
788
 
789
                    button = next;
790
 
791
                    if (next)
792
                        next->previous = prev;
793
 
794
                    turned = true;
795
                    continue;
796
                }
797
            }
798
 
799
            button = button->next;
800
        }
801
    }
802
 
803
    return true;
804
}
204 andreas 805
 
289 andreas 806
void TPageInterface::setFonts(TFont *font)
807
{
808
    DECL_TRACER("TPageInterface::setFonts(TFont *font)");
809
 
810
    if (!font)
811
        return;
812
 
813
    mFonts = font;
814
 
815
    Button::BUTTONS_T *button = mButtons;
816
 
817
    while (button)
818
    {
819
        button->button->setFonts(font);
820
        button = button->next;
821
    }
822
}
823
 
204 andreas 824
vector<string> TPageInterface::getListContent(ulong handle, int ap, int ta, int ti, int rows, int columns)
825
{
826
    DECL_TRACER("TPageInterface::getListContent(ulong handle, int ap, int ta, int ti, int rows, int columns)");
827
 
828
    if (ap == 0 && ta == 0 && ti == 0)
829
    {
830
        vector<LIST_t>::iterator iter;
831
 
832
        for (iter = mLists.begin(); iter != mLists.end(); ++iter)
833
        {
834
            if (iter->handle == handle)
835
            {
836
                return iter->list;
837
            }
838
        }
839
 
840
        return vector<string>();
841
    }
842
 
206 andreas 843
    if (ap == 0 && (ta == SYSTEM_LIST_SYSTEMSOUND || ta == SYSTEM_LIST_SINGLEBEEP)) // System listbox: system sounds and system single beeps
204 andreas 844
    {
205 andreas 845
        vector<LIST_t>::iterator iter;
204 andreas 846
 
205 andreas 847
        for (iter = mLists.begin(); iter != mLists.end(); ++iter)
204 andreas 848
        {
205 andreas 849
            if (iter->handle == handle)
850
            {
851
                iter->ap = ap;
852
                iter->ta = ta;
853
                iter->ti = ti;
854
                iter->rows = rows;
855
                iter->columns = columns;
204 andreas 856
 
205 andreas 857
                if (iter->selected < 0 && !iter->list.empty())
858
                {
859
                    int row = getSystemSelection(ta, iter->list);
204 andreas 860
 
205 andreas 861
                    if (row > 0)
862
                        iter->selected = row;
863
                }
204 andreas 864
 
205 andreas 865
                return iter->list;
866
            }
204 andreas 867
        }
868
 
205 andreas 869
        TSystemSound sysSound(TConfig::getSystemProjectPath() + "/graphics/sounds");
870
        vector<string> tmpFiles = sysSound.getAllSingleBeep();
871
        LIST_t list;
872
        list.handle = handle;
873
        list.ap = ap;
874
        list.ta = ta;
875
        list.ti = ti;
876
        list.rows = rows;
877
        list.columns = columns;
878
        list.list = tmpFiles;
879
        list.selected = getSystemSelection(ta, tmpFiles);
880
        mLists.push_back(list);
881
        return tmpFiles;
882
    }
206 andreas 883
    else if (ap == 0 && ta == SYSTEM_LIST_DOUBLEBEEP)   // System listbox: double beeps
205 andreas 884
    {
204 andreas 885
        vector<LIST_t>::iterator iter;
886
 
887
        for (iter = mLists.begin(); iter != mLists.end(); ++iter)
888
        {
889
            if (iter->handle == handle)
890
            {
891
                iter->ap = ap;
892
                iter->ta = ta;
893
                iter->ti = ti;
894
                iter->rows = rows;
895
                iter->columns = columns;
205 andreas 896
 
897
                if (iter->selected < 0 && !iter->list.empty())
898
                {
899
                    int row = getSystemSelection(ta, iter->list);
900
 
901
                    if (row > 0)
902
                        iter->selected = row;
903
                }
904
 
905
                return iter->list;
204 andreas 906
            }
907
        }
908
 
205 andreas 909
        TSystemSound sysSound(TConfig::getSystemProjectPath() + "/graphics/sounds");
910
        vector<string> tmpFiles = sysSound.getAllDoubleBeep();
204 andreas 911
        LIST_t list;
912
        list.handle = handle;
913
        list.ap = ap;
914
        list.ta = ta;
915
        list.ti = ti;
916
        list.rows = rows;
917
        list.columns = columns;
918
        list.list = tmpFiles;
205 andreas 919
        list.selected = getSystemSelection(ta, tmpFiles);
204 andreas 920
        mLists.push_back(list);
921
        return tmpFiles;
922
    }
206 andreas 923
    else if (ap == 0 && ta == SYSTEM_LIST_SURFACE)  // System listbox: TP4 file (surface file)
924
    {
925
        vector<LIST_t>::iterator iter;
204 andreas 926
 
206 andreas 927
        for (iter = mLists.begin(); iter != mLists.end(); ++iter)
928
        {
929
            if (iter->handle == handle)
930
            {
931
                iter->ap = ap;
932
                iter->ta = ta;
933
                iter->ti = ti;
934
                iter->rows = rows;
935
                iter->columns = columns;
936
 
937
                if (iter->selected < 0 && !iter->list.empty())
938
                {
939
                    int row = getSystemSelection(ta, iter->list);
940
 
941
                    if (row > 0)
942
                        iter->selected = row;
943
                }
944
 
945
                return iter->list;
946
            }
947
        }
948
 
949
        // Load surface file names from NetLinx over FTP
950
        TTPInit tt;
951
        vector<TTPInit::FILELIST_t> fileList = tt.getFileList(".tp4");
952
        vector<string> tmpFiles;
953
 
954
        if (!fileList.empty())
955
        {
956
            vector<TTPInit::FILELIST_t>::iterator iter;
957
 
958
            if (gPageManager)
959
                gPageManager->clearFtpSurface();
960
 
961
            for (iter = fileList.begin(); iter != fileList.end(); ++iter)
962
            {
963
                tmpFiles.push_back(iter->fname);
964
 
965
                if (gPageManager)
966
                    gPageManager->addFtpSurface(iter->fname, iter->size);
967
            }
968
        }
969
 
970
        LIST_t list;
971
        list.handle = handle;
972
        list.ap = ap;
973
        list.ta = ta;
974
        list.ti = ti;
975
        list.rows = rows;
976
        list.columns = columns;
977
        list.list = tmpFiles;
978
        list.selected = getSystemSelection(ta, tmpFiles);
979
        mLists.push_back(list);
980
        return tmpFiles;
981
    }
982
 
204 andreas 983
    return vector<string>();
984
}
985
 
205 andreas 986
int TPageInterface::getSystemSelection(int ta, vector<string>& list)
987
{
988
    DECL_TRACER("TPageInterface::setSystemSelection(int ta, vector<string>* list)");
989
 
990
    vector<string>::iterator iterSel;
991
    string sel;
992
 
206 andreas 993
    if (ta == SYSTEM_LIST_SURFACE)
994
        sel = TConfig::getFtpSurface();
995
    if (ta == SYSTEM_LIST_SYSTEMSOUND)
205 andreas 996
        sel = TConfig::getSystemSound();
206 andreas 997
    else if (ta == SYSTEM_LIST_SINGLEBEEP)
205 andreas 998
        sel = TConfig::getSingleBeepSound();
206 andreas 999
    else if (ta == SYSTEM_LIST_DOUBLEBEEP)
205 andreas 1000
        sel = TConfig::getDoubleBeepSound();
1001
    else
1002
        return -1;
1003
 
1004
    int row = 1;
1005
 
1006
    for (iterSel = list.begin(); iterSel != list.end(); ++iterSel)
1007
    {
1008
        if (iterSel->compare(sel) == 0)
1009
            return row;
1010
 
1011
        row++;
1012
    }
1013
 
1014
    return -1;
1015
}
1016
 
204 andreas 1017
string TPageInterface::getListRow(int ti, int row)
1018
{
1019
    DECL_TRACER("TPageInterface::getListRow(ulong handle, int ti, int row)");
1020
 
1021
    vector<LIST_t>::iterator iter;
1022
 
1023
    for (iter = mLists.begin(); iter != mLists.end(); ++iter)
1024
    {
1025
        if (iter->ti == ti)
1026
        {
1027
            if (row < 1 || (size_t)row > iter->list.size())
1028
                return string();
1029
 
1030
            return iter->list[row-1];
1031
        }
1032
    }
1033
 
1034
    return string();
1035
}
1036
 
1037
void TPageInterface::setGlobalSettings(Button::TButton* button)
1038
{
1039
    DECL_TRACER("TPageInterface::setGlobalSettings(TButton* button)");
1040
 
1041
    if (!button)
1042
        return;
1043
 
205 andreas 1044
    button->setFontOnly(sr[0].fi, 0);
1045
    button->setTextColorOnly(sr[0].ct, 0);
1046
    button->setTextEffectColorOnly(sr[0].ec, 0);
204 andreas 1047
 
206 andreas 1048
    if (button->getListAp() == 0 && button->getListTi() >= SYSTEM_PAGE_START)
205 andreas 1049
        button->setTextJustificationOnly(4, 0, 0, 0);
204 andreas 1050
}
205 andreas 1051
 
1052
void TPageInterface::setSelectedRow(ulong handle, int row)
1053
{
1054
    DECL_TRACER("TPageInterface::setSelectedRow(ulong handle, int row)");
1055
 
1056
    if (row < 1)
1057
        return;
1058
 
1059
    vector<LIST_t>::iterator iter;
1060
 
1061
    for (iter = mLists.begin(); iter != mLists.end(); ++iter)
1062
    {
1063
        if (iter->handle == handle)
1064
        {
1065
            if ((size_t)row <= iter->list.size())
1066
                iter->selected = row;
1067
 
271 andreas 1068
            MSG_DEBUG("Row was set to " << row << " for item " << handleToString(handle));
205 andreas 1069
            return;
1070
        }
1071
    }
1072
}
1073
 
1074
int TPageInterface::getSelectedRow(ulong handle)
1075
{
1076
    DECL_TRACER("TPageInterface::getSelectedRow(ulong handle)");
1077
 
1078
    vector<LIST_t>::iterator iter;
1079
 
1080
    for (iter = mLists.begin(); iter != mLists.end(); ++iter)
1081
    {
1082
        if (iter->handle == handle)
1083
            return iter->selected;
1084
    }
1085
 
1086
    return -1;
1087
}
1088
 
1089
string TPageInterface::getSelectedItem(ulong handle)
1090
{
1091
    DECL_TRACER("TPageInterface::getSelectedItem(ulong handle)");
1092
 
1093
    vector<LIST_t>::iterator iter;
1094
 
1095
    for (iter = mLists.begin(); iter != mLists.end(); ++iter)
1096
    {
1097
        if (iter->handle == handle)
1098
        {
1099
            if (iter->selected > 0 && (size_t)iter->selected <= iter->list.size())
1100
                return iter->list[iter->selected-1];
1101
 
1102
            ulong nPage = (handle >> 16) & 0x0000ffff;
1103
            ulong nButt = handle & 0x0000ffff;
1104
            string sel;
1105
 
206 andreas 1106
            if (nPage == SYSTEM_SUBPAGE_SURFACE && nButt == 1)
1107
                sel = TConfig::getFtpSurface();
1108
            if (nPage == SYSTEM_SUBPAGE_SYSTEMSOUND && nButt == 1)
205 andreas 1109
                sel = TConfig::getSystemSound();
206 andreas 1110
            else if (nPage == SYSTEM_SUBPAGE_SINGLEBEEP && nButt == 1)
205 andreas 1111
                sel = TConfig::getSingleBeepSound();
206 andreas 1112
            else if (nPage == SYSTEM_SUBPAGE_DOUBLEBEEP && nButt == 1)
205 andreas 1113
                sel = TConfig::getDoubleBeepSound();
1114
            else
1115
                return string();
1116
 
1117
            if (iter->list.empty())
1118
                return string();
1119
 
1120
            vector<string>::iterator iterSel;
1121
            int row = 1;
1122
 
1123
            for (iterSel = iter->list.begin(); iterSel != iter->list.end(); ++iterSel)
1124
            {
1125
                if (iterSel->compare(sel) == 0)
1126
                {
1127
                    iter->selected = row;
1128
                    return sel;
1129
                }
1130
 
1131
                row++;
1132
            }
1133
        }
1134
    }
1135
 
1136
    return string();
1137
}
258 andreas 1138
 
404 andreas 1139
/**
1140
 * @brief getBorderFragment - get part of border
1141
 * The method reads a border image fragment from the disk and converts it to
1142
 * the border color. If there is a base image and an alpha mask image, the
1143
 * pixels of the alpha mask are converted to the border color and then the base
1144
 * image is layed over the mask image.
1145
 * In case there is no base image, an image with the same size as the mask image
1146
 * is created and filled transparaent.
1147
 *
1148
 * @param path      The path and file name of the base image.
1149
 * @param pathAlpha The path and file name of the alpha mask image.
1150
 * @param image     A pointer to an empty bitmap.
1151
 * @param color     The border color
1152
 *
1153
 * @return In case the images exists and were loaded successfully, TRUE is
1154
 * returned.
1155
 */
1156
bool TPageInterface::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)
1157
{
1158
    DECL_TRACER("TPageInterface::getBorderFragment(const string& path, const string& pathAlpha, SkBitmap* image, SkColor color)");
1159
 
1160
    if (!image)
1161
    {
1162
        MSG_ERROR("Invalid pointer to image!");
1163
        return false;
1164
    }
1165
 
1166
    sk_sp<SkData> im;
1167
    SkBitmap bm;
1168
    bool haveBaseImage = false;
1169
 
1170
    // If the path ends with "alpha.png" then it is a mask image. This not what
1171
    // we want first unless this is the only image available.
1172
    if (!endsWith(path, "alpha.png") || pathAlpha.empty())
1173
    {
1174
        if (retrieveImage(path, image))
1175
        {
1176
            haveBaseImage = true;
1177
            // Underly the pixels with the border color
1178
            MSG_DEBUG("Path: " << path << ", pathAlpha: " << pathAlpha);
1179
            if (pathAlpha.empty() || !fs::exists(pathAlpha) || path == pathAlpha)
1180
            {
1181
                SkImageInfo info = image->info();
1182
                SkBitmap b;
1183
                allocPixels(info.width(), info.height(), &b);
1184
                b.eraseColor(SK_ColorTRANSPARENT);
1185
 
1186
                for (int x = 0; x < info.width(); ++x)
1187
                {
1188
                    for (int y = 0; y < info.height(); ++y)
1189
                    {
1190
                        SkColor alpha = SkColorGetA(image->getColor(x, y));
1191
                        uint32_t *pix = b.getAddr32(x, y);
1192
 
1193
                        if (alpha > 0)
1194
                            *pix = color;
1195
                    }
1196
                }
1197
 
1198
                SkPaint paint;
1199
                paint.setAntiAlias(true);
1200
                paint.setBlendMode(SkBlendMode::kDstATop);
1201
                SkCanvas can(*image);
1202
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(b);
1203
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
1204
            }
1205
        }
1206
    }
1207
 
1208
    // If there is no valid path return.
1209
    if (pathAlpha.empty())
1210
        return haveBaseImage;
1211
 
1212
    // On error retrieving the image, return.
1213
    if (!retrieveImage(pathAlpha, &bm))
1214
        return haveBaseImage;
1215
 
1216
    // If there was no base image loaded, allocate the space for an image
1217
    // filled transparent. Make it the same size as the mask image.
1218
    if (!haveBaseImage)
1219
    {
1220
        allocPixels(bm.info().width(), bm.info().height(), image);
1221
        image->eraseColor(SK_ColorTRANSPARENT);
1222
    }
1223
 
1224
    // Only if the base image and the mask image have the same size, which
1225
    // should be the case, then the visible pixels of the mask image are
1226
    // colored by the border color.
1227
    if (image->info().dimensions() == bm.info().dimensions())
1228
    {
1229
        for (int y = 0; y < image->info().height(); ++y)
1230
        {
1231
            for (int x = 0; x < image->info().width(); ++x)
1232
            {
1233
                SkColor col = bm.getColor(x, y);
1234
                SkColor alpha = SkColorGetA(col);
1235
                uint32_t *pix = bm.getAddr32(x, y);
1236
 
1237
                if (alpha == 0)
1238
                    *pix = SK_ColorTRANSPARENT;
1239
                else
1240
                    *pix = SkColorSetA(color, alpha);
1241
            }
1242
        }
1243
    }
1244
 
1245
    // Here we draw the border fragment over the base image.
1246
    SkPaint paint;
1247
    paint.setAntiAlias(true);
1248
    paint.setBlendMode(SkBlendMode::kDstATop);
1249
    SkCanvas can(*image);
1250
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bm);
1251
    can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
1252
 
1253
    return true;
1254
}
1255
 
258 andreas 1256
SkBitmap TPageInterface::retrieveBorderImage(const string& pa, const string& pb, SkColor color, SkColor bgColor)
1257
{
1258
    DECL_TRACER("TPageInterface::retrieveBorderImage(const string& pa, const string& pb, SkColor color, SkColor bgColor)");
1259
 
1260
    SkBitmap bm, bma;
1261
 
1262
    if (!pa.empty() && !retrieveImage(pa, &bm))
1263
        return SkBitmap();
1264
 
1265
    if (!pb.empty() && !retrieveImage(pb, &bma))
1266
        return SkBitmap();
1267
 
1268
    return colorImage(bm, bma, color, bgColor, false);
1269
}
1270
 
1271
bool TPageInterface::retrieveImage(const string& path, SkBitmap* image)
1272
{
1273
    DECL_TRACER("TPageInterface::retrieveImage(const string& path, SkBitmap* image)");
1274
 
1275
    sk_sp<SkData> im;
1276
 
1277
    if (!(im = readImage(path)))
1278
        return false;
1279
 
1280
    DecodeDataToBitmap(im, image);
1281
 
1282
    if (image->empty())
1283
    {
1284
        MSG_WARNING("Could not create the image " << path);
1285
        return false;
1286
    }
1287
 
1288
    return true;
1289
}
1290
 
1291
SkBitmap TPageInterface::colorImage(SkBitmap& base, SkBitmap& alpha, SkColor col, SkColor bg, bool useBG)
1292
{
1293
    DECL_TRACER("TPageInterface::colorImage(SkBitmap *img, int width, int height, SkColor col, SkColor bg, bool useBG)");
1294
 
1295
    int width = base.info().width();
1296
    int height = base.info().height();
1297
 
1298
    if (width <= 0 || height <= 0)
1299
    {
1300
        MSG_WARNING("Got invalid width or height! (width: " << width << ", height: " << height << ")");
1301
        return SkBitmap();
1302
    }
1303
 
1304
    if (!alpha.empty())
1305
    {
1306
        if (width != alpha.info().width() || height != alpha.info().height())
1307
        {
1308
            MSG_ERROR("Base and alpha masks have different size!");
1309
            return SkBitmap();
1310
        }
1311
    }
1312
 
1313
    SkBitmap maskBm;
1314
 
1315
    if (!allocPixels(width, height, &maskBm))
1316
        return SkBitmap();
1317
 
1318
    maskBm.eraseColor(SK_ColorTRANSPARENT);
1319
 
1320
    for (int ix = 0; ix < width; ix++)
1321
    {
1322
        for (int iy = 0; iy < height; iy++)
1323
        {
1324
            SkColor pixelAlpha = 0;
1325
 
1326
            if (!alpha.empty())
1327
                pixelAlpha = alpha.getColor(ix, iy);
1328
            else
1329
                pixelAlpha = base.getColor(ix, iy);
1330
 
1331
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
1332
 
1333
            if (!wpix)
1334
            {
1335
                MSG_ERROR("No pixel buffer!");
1336
                break;
1337
            }
1338
 
1339
            uint32_t ala = SkColorGetA(pixelAlpha);
1340
 
1341
            if (ala == 0 && !useBG)
1342
                pixelAlpha = col;
1343
            else if (ala == 0)
1344
                pixelAlpha = bg;
1345
            else
1346
            {
1347
                // We've to change the red and the blue color channel because
1348
                // of an error in the Skia library.
1349
                uint32_t red = SkColorGetR(col);
1350
                uint32_t green = SkColorGetG(col);
1351
                uint32_t blue = SkColorGetB(col);
1352
 
1353
                if (alpha.empty())
1354
                {
1355
                    uint32_t pred = SkColorGetR(pixelAlpha);
1356
                    uint32_t pgreen = SkColorGetG(pixelAlpha);
1357
                    uint32_t pblue = SkColorGetB(pixelAlpha);
1358
                    uint32_t maxChan = SkColorGetG(SK_ColorWHITE);
1359
 
1360
                    red   = ((pred == maxChan) ? pred : red);
1361
                    green = ((pgreen == maxChan) ? pgreen : green);
1362
                    blue  = ((pblue == maxChan) ? pblue : blue);
1363
                }
1364
                else if (ala == 0)
1365
                    red = green = blue = 0;
1366
 
1367
                pixelAlpha = SkColorSetARGB(ala, red, green, blue);
1368
            }
1369
 
1370
            *wpix = pixelAlpha;
1371
        }
1372
    }
1373
 
1374
    if (!alpha.empty())
1375
    {
1376
        SkPaint paint;
1377
        paint.setBlendMode(SkBlendMode::kSrcOver);
1378
        SkCanvas can(maskBm);
365 andreas 1379
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(base);
258 andreas 1380
        can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
1381
    }
1382
 
1383
    return maskBm;
1384
}
1385
 
1386
bool TPageInterface::stretchImageWidth(SkBitmap *bm, int width)
1387
{
1388
    DECL_TRACER("TPageInterface::stretchImageWidth(SkBitmap *bm, int width)");
1389
 
1390
    if (!bm)
1391
        return false;
1392
 
1393
    int rwidth = width;
1394
    SkPaint paint;
1395
    paint.setBlendMode(SkBlendMode::kSrc);
1396
 
1397
    SkImageInfo info = bm->info();
365 andreas 1398
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
258 andreas 1399
 
1400
    if (width <= 0)
1401
        rwidth = info.width() + width;
1402
 
1403
    if (rwidth <= 0)
1404
        rwidth = 1;
1405
 
1406
    MSG_DEBUG("Width: " << rwidth << ", Height: " << info.height());
1407
 
1408
    if (!allocPixels(rwidth, info.height(), bm))
1409
        return false;
1410
 
1411
    bm->eraseColor(SK_ColorTRANSPARENT);
1412
    SkCanvas can(*bm, SkSurfaceProps());
1413
    SkRect rect = SkRect::MakeXYWH(0, 0, rwidth, info.height());
1414
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
1415
    return true;
1416
}
1417
 
1418
bool TPageInterface::stretchImageHeight(SkBitmap *bm, int height)
1419
{
1420
    DECL_TRACER("TPageInterface::stretchImageHeight(SkBitmap *bm, int height)");
1421
 
1422
    if (!bm)
1423
        return false;
1424
 
1425
    int rheight = height;
1426
    SkPaint paint;
1427
    paint.setBlendMode(SkBlendMode::kSrc);
1428
 
1429
    SkImageInfo info = bm->info();
1430
 
1431
    if (height <= 0)
1432
        rheight = info.height() + height;
1433
 
1434
    if (rheight <= 0)
1435
        rheight = 1;
1436
 
365 andreas 1437
    sk_sp<SkImage> im = SkImages::RasterFromBitmap(*bm);
258 andreas 1438
    MSG_DEBUG("Width: " << info.width() << ", Height: " << rheight);
1439
 
1440
    if (!allocPixels(info.width(), rheight, bm))
1441
        return false;
1442
 
1443
    bm->eraseColor(SK_ColorTRANSPARENT);
1444
    SkCanvas can(*bm, SkSurfaceProps());
1445
    SkRect rect = SkRect::MakeXYWH(0, 0, info.width(), rheight);
1446
    can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
1447
    return true;
1448
}
1449
 
262 andreas 1450
#ifdef _OPAQUE_SKIA_
258 andreas 1451
bool TPageInterface::setOpacity(SkBitmap *bm, int oo)
1452
{
1453
    DECL_TRACER("TPageInterface::setOpacity(SkBitmap *bm, int oo)");
1454
 
1455
    if (oo < 0 || oo > 255 || !bm)
1456
        return false;
1457
 
1458
    SkBitmap ooButton;
1459
    int w = bm->info().width();
1460
    int h = bm->info().height();
1461
 
1462
    if (!allocPixels(w, h, &ooButton))
1463
        return false;
1464
 
1465
    SkCanvas canvas(ooButton);
1466
    SkIRect irect = SkIRect::MakeXYWH(0, 0, w, h);
1467
    SkRegion region;
1468
    region.setRect(irect);
1469
    SkScalar opaque = (SkScalar)oo;
1470
 
1471
    SkScalar alpha = 1.0 / 255.0 * opaque;
1472
    MSG_DEBUG("Calculated alpha value: " << alpha << " (oo=" << oo << ")");
1473
    SkPaint paint;
1474
    paint.setAlphaf(alpha);
365 andreas 1475
    sk_sp<SkImage> _image = SkImages::RasterFromBitmap(*bm);
258 andreas 1476
    canvas.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
1477
    bm->erase(SK_ColorTRANSPARENT, {0, 0, w, h});
1478
    *bm = ooButton;
1479
    return true;
262 andreas 1480
}
1481
#endif