Subversion Repositories tpanel

Rev

Rev 446 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
446 andreas 1
/*
486 andreas 2
 * Copyright (C) 2021 to 2025 by Andreas Theofilu <andreas@theosys.at>
446 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 <include/core/SkBitmap.h>
20
 
21
#include "tdrawimage.h"
22
#include "tbutton.h"
23
#include "terror.h"
486 andreas 24
#include "ttpinit.h"
25
#include "timgcache.h"
446 andreas 26
#include <qglobal.h>
27
 
28
extern bool prg_stopped;
29
 
30
using std::string;
31
using std::vector;
32
 
33
TDrawImage::TDrawImage()
34
{
35
    DECL_TRACER("TDrawImage::TDrawImage()");
36
}
37
 
38
TDrawImage::~TDrawImage()
39
{
40
    DECL_TRACER("TDrawImage::~TDrawImage()");
486 andreas 41
 
42
    if (TTPInit::isTP5() && !mBitmapStack.empty())
43
        mBitmapStack.clear();
446 andreas 44
}
45
 
486 andreas 46
void TDrawImage::setImageBm(SkBitmap& bm)
47
{
48
    DECL_TRACER("TDrawImage::setImageBm(SkBitmap& bm)");
49
 
50
    if (TTPInit::isTP5())
51
    {
52
        int width = bm.info().width();
53
        int height = bm.info().height();
54
        size_t index = mBitmapStack.empty() ? 0 : (mBitmapStack.size() - 1);
55
 
56
        mBitmapStack.push_back(bm);         // Put image on the stack
57
 
58
        if (imageBm.empty())                // If this is the 1st image, allocate the space for it
59
        {
60
            imageBm.allocN32Pixels(width, height);
61
            imageBm.eraseColor(SK_ColorTRANSPARENT);
62
        }
63
 
64
        MSG_DEBUG("Image info: Width: " << width << ", Height: " << height << ", Instance: " << mInstance << ", Index: " << index);
65
        Button::POSITION_t position = calcImagePosition(width, height, mInstance, index);
66
 
67
        if (!position.valid)
68
        {
69
            MSG_ERROR("Error calculating the position of the image!");
70
            TError::setError();
71
            return;
72
        }
73
        // Draw the image to the target. If there was a previous one, the new one is blended over
74
        SkPaint paint;
75
        SkCanvas can(imageBm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
76
        paint.setBlendMode(SkBlendMode::kSrcOver);
77
 
78
        if (mSr[mInstance].sb == 0)
79
        {
80
            sk_sp<SkImage> _image = SkImages::RasterFromBitmap(bm);
81
            can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
82
        }
83
        else    // Scale to fit
84
        {
85
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
86
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(bm);
87
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
88
        }
89
    }
90
    else
91
        imageBm = bm;
92
}
93
 
94
SkBitmap& TDrawImage::getImageBm(size_t index)
95
{
96
    DECL_TRACER("TDrawImage::getImageBm(size_t index)");
97
 
98
    if (TTPInit::isTP5())
99
    {
100
        if (index >= mBitmapStack.size())
101
            return imageBm;
102
 
103
        return mBitmapStack[index];
104
    }
105
 
106
    return imageBm;
107
}
108
 
446 andreas 109
bool TDrawImage::drawImage(SkBitmap* bm)
110
{
111
    DECL_TRACER("TDrawImage::drawImage(SkBitmap* bm)");
112
 
113
    if (prg_stopped)
114
        return false;
115
 
116
    if (mSr.size() == 0)
117
    {
118
        MSG_ERROR("No SR information!");
119
        return false;
120
    }
121
 
122
    int instance = mInstance;
123
 
124
    if (mInstance < 0)
125
        instance = 0;
126
    else if ((size_t)mInstance >= mSr.size())
127
        instance = mSr.size() - 1;
128
 
129
    if (mSr[instance].dynamic)
130
    {
131
        MSG_WARNING("Dynamic images are not handled by TDrawImage!");
132
        return false;
133
    }
134
 
135
    if (!imageMi.empty() && !mSr[instance].mi.empty() && mSr[instance].bs.empty())       // Chameleon image?
136
    {
137
        MSG_TRACE("Chameleon image ...");
138
        SkBitmap imgRed(imageMi);
139
        SkBitmap imgMask;
140
 
486 andreas 141
        if ((!TTPInit::isTP5() && !imageBm.empty() && !mSr[instance].bm.empty()) || (TTPInit::isTP5() && !imageBm.empty()))
446 andreas 142
            imgMask.installPixels(imageBm.pixmap());
143
        else
144
        {
145
            imgMask.allocN32Pixels(mSr[instance].mi_width, mSr[instance].mi_height);
146
            imgMask.eraseColor(SK_ColorTRANSPARENT);
147
        }
148
 
149
        SkBitmap img = drawImageButton(imgRed, imgMask, mSr[instance].mi_width, mSr[instance].mi_height, TColor::getSkiaColor(mSr[instance].cf), TColor::getSkiaColor(mSr[instance].cb));
150
 
151
        if (img.empty())
152
        {
153
            MSG_ERROR("Error creating the cameleon image \"" << mSr[instance].mi << "\" / \"" << mSr[instance].bm << "\"!");
154
            TError::setError();
155
            return false;
156
        }
157
 
158
        SkCanvas ctx(img, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
159
        SkImageInfo info = img.info();
160
        SkPaint paint;
161
        paint.setBlendMode(SkBlendMode::kSrcOver);
162
        sk_sp<SkImage> _image = SkImages::RasterFromBitmap(imgMask);
163
        ctx.drawImage(_image, 0, 0, SkSamplingOptions(), &paint);
164
 
486 andreas 165
        Button::POSITION_t position = calcImagePosition(mSr[instance].mi_width, mSr[instance].mi_height, instance, mBitmapStack.size() - 1);
446 andreas 166
 
167
        if (!position.valid)
168
        {
169
            MSG_ERROR("Error calculating the position of the image!");
170
            TError::setError();
171
            return false;
172
        }
173
 
174
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
175
        paint.setBlendMode(SkBlendMode::kSrc);
176
 
177
        if (mSr[instance].sb == 0)
178
        {
179
            sk_sp<SkImage> _image = SkImages::RasterFromBitmap(img);
180
            can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
181
        }
182
        else    // Scale to fit
183
        {
184
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
185
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(img);
186
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
187
        }
188
    }
486 andreas 189
    else if ((!TTPInit::isTP5() &&!imageBm.empty() && !mSr[instance].bm.empty()) || (TTPInit::isTP5() &&!imageBm.empty()))
446 andreas 190
    {
191
        MSG_TRACE("Drawing normal image ...");
192
        SkBitmap image = imageBm;
193
 
194
        if (image.empty())
195
        {
486 andreas 196
            if (TTPInit::isTP5())
197
            {
198
                MSG_ERROR("Error creating the image \"" << mSr[instance].bitmaps[mBitmapStack.size()-1].fileName << "\"!");
199
            }
200
            else
201
            {
202
                MSG_ERROR("Error creating the image \"" << mSr[instance].bm << "\"!");
203
            }
204
 
446 andreas 205
            TError::setError();
206
            return false;
207
        }
208
 
486 andreas 209
        Button::POSITION_t position;
446 andreas 210
 
486 andreas 211
        if (!TTPInit::isTP5())
212
            position = calcImagePosition(mSr[instance].bm_width, mSr[instance].bm_height, instance);
213
        else
214
            position = calcImagePosition(imageBm.info().width(), imageBm.info().height(), instance);
215
 
446 andreas 216
        if (!position.valid)
217
        {
218
            MSG_ERROR("Error calculating the position of the image.");
219
            TError::setError();
220
            return false;
221
        }
222
 
223
        MSG_DEBUG("Putting bitmap on top of image ...");
224
        SkPaint paint;
225
        paint.setBlendMode(SkBlendMode::kSrcOver);
226
        SkCanvas can(*bm, SkSurfaceProps(1, kUnknown_SkPixelGeometry));
227
 
228
        if (mSr[instance].sb == 0)
229
        {
486 andreas 230
            size_t index = mBitmapStack.size() > 0 ? (mBitmapStack.size() - 1) : 0;
231
            Button::BITMAPS_t bitmap;
232
 
233
            if (mSr[instance].bitmaps.size() > index)
234
                bitmap = mSr[instance].bitmaps[index];
235
 
236
            if ((!TTPInit::isTP5() && ((mSr[instance].jb == 0 && mSr[instance].bx >= 0 && mSr[instance].by >= 0) || mSr[instance].jb != 0)) ||  // Draw the full image
237
                (TTPInit::isTP5() && ((bitmap.justification == 0 && bitmap.offsetX >= 0 && bitmap.offsetY >= 0) || bitmap.justification != 0)))
446 andreas 238
            {
239
                MSG_DEBUG("Drawing full image ...");
240
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(image);
241
                can.drawImage(_image, position.left, position.top, SkSamplingOptions(), &paint);
242
            }
243
            else    // We need only a subset of the image
244
            {
245
                MSG_DEBUG("Create a subset of an image ...");
246
 
247
                // Create a new Info to have the size of the subset.
248
                SkImageInfo info = SkImageInfo::Make(position.width, position.height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
249
                size_t byteSize = info.computeMinByteSize();
250
 
251
                if (byteSize == 0)
252
                {
253
                    MSG_ERROR("Unable to calculate size of image!");
254
                    TError::setError();
255
                    return false;
256
                }
257
 
258
                MSG_DEBUG("Rectangle of part: x: " << position.left << ", y: " << position.top << ", w: " << position.width << ", h: " << position.height);
259
                SkBitmap part;      // Bitmap receiving the wanted part from the whole image
260
                SkIRect irect = SkIRect::MakeXYWH(position.left, position.top, position.width, position.height);
261
                image.extractSubset(&part, irect);  // Extract the part of the image containg the pixels we want
262
                sk_sp<SkImage> _image = SkImages::RasterFromBitmap(part);
263
                can.drawImage(_image, 0, 0, SkSamplingOptions(), &paint); // Draw the image
264
            }
265
        }
266
        else    // Scale to fit
267
        {
268
            MSG_DEBUG("Scaling image to fit.");
269
            SkRect rect = SkRect::MakeXYWH(position.left, position.top, position.width, position.height);
270
            sk_sp<SkImage> im = SkImages::RasterFromBitmap(image);
271
            can.drawImageRect(im, rect, SkSamplingOptions(), &paint);
272
        }
273
    }
274
    else if ((imageBm.empty() && !mSr[0].bm.empty()) || (imageMi.empty() && !mSr[0].mi.empty()))
275
    {
276
        if (imageBm.empty())
277
        {
278
            MSG_ERROR("Image BM " << mSr[0].bm << " defined but got no image!");
279
        }
280
 
281
        if (imageMi.empty())
282
        {
283
            MSG_ERROR("Image MI " << mSr[0].mi << " defined but got no image!");
284
        }
285
 
286
        return false;
287
    }
288
    else
289
    {
290
        MSG_DEBUG("No bitmap defined.");
291
    }
292
 
293
    return true;
294
}
295
 
296
SkBitmap TDrawImage::drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2)
297
{
298
    DECL_TRACER("TDrawImage::drawImageButton(SkImage& imgRed, SkImage& imgMask, int width, int height, SkColor col1, SkColor col2)");
299
 
300
    if (width <= 0 || height <= 0)
301
    {
302
        MSG_WARNING("Got invalid width of height! (width: " << width << ", height: " << height);
303
        return SkBitmap();
304
    }
305
 
306
    SkPixmap pixmapRed = imgRed.pixmap();
307
    SkPixmap pixmapMask;
308
 
309
    if (!imgMask.empty())
310
        pixmapMask = imgMask.pixmap();
311
 
312
    SkImageInfo maskPixInfo = SkImageInfo::MakeN32Premul(width, height);
313
    maskPixInfo.makeColorType(kRGBA_8888_SkColorType);
314
    SkBitmap maskBm;
315
    maskBm.allocPixels(SkImageInfo::MakeN32Premul(width, height));
316
 
317
    for (int ix = 0; ix < width; ix++)
318
    {
319
        for (int iy = 0; iy < height; iy++)
320
        {
321
            SkColor pixelRed = pixmapRed.getColor(ix, iy);
322
            SkColor pixelMask;
323
 
324
            if (!imgMask.empty())
325
                pixelMask = pixmapMask.getColor(ix, iy);
326
            else
327
                pixelMask = SK_ColorWHITE;
328
 
329
            SkColor pixel = baseColor(pixelRed, pixelMask, col1, col2);
330
            uint32_t alpha = SkColorGetA(pixel);
331
            uint32_t *wpix = maskBm.getAddr32(ix, iy);
332
 
333
            if (!wpix)
334
            {
335
                MSG_ERROR("No pixel buffer!");
336
                break;
337
            }
338
 
339
            if (alpha == 0)
340
                pixel = pixelMask;
341
            // Skia has a bug and has changed the red and the blue color
342
            // channel. Therefor we must change this 2 color channels for
343
            // Linux based OSs here. On Android this is not necessary.
344
//#ifdef __ANDROID__
345
            *wpix = pixel;
346
//#else   // We've to invert the pixels here to have the correct colors
347
//            uchar red   = SkColorGetR(pixel);   // This is blue in reality
348
//            uchar green = SkColorGetG(pixel);
349
//            uchar blue  = SkColorGetB(pixel);   // This is red in reality
350
//            uchar al    = SkColorGetA(pixel);
351
//            *wpix = SkColorSetARGB(al, blue, green, red);
352
//#endif
353
        }
354
    }
355
 
356
    return maskBm;
357
}
358
 
359
SkColor TDrawImage::baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2)
360
{
361
    uint alpha = SkColorGetA(basePix);
362
    uint green = SkColorGetG(basePix);
363
#ifndef __ANDROID__
364
    uint red = SkColorGetR(basePix);
365
#else
366
    uint red = SkColorGetB(basePix);
367
#endif
368
 
369
    if (alpha == 0)
370
        return maskPix;
371
 
372
    if (red && green)
373
    {
374
//#ifdef __ANDROID__
375
//        uint newB = (SkColorGetR(col1) + SkColorGetR(col2) / 2) & 0x0ff;
376
//        uint newG = (SkColorGetG(col1) + SkColorGetG(col2) / 2) & 0x0ff;
377
//        uint newR = (SkColorGetB(col1) + SkColorGetB(col2) / 2) & 0x0ff;
378
//#else
379
        uint newR = (SkColorGetR(col1) + SkColorGetR(col2) / 2) & 0x0ff;
380
        uint newG = (SkColorGetG(col1) + SkColorGetG(col2) / 2) & 0x0ff;
381
        uint newB = (SkColorGetB(col1) + SkColorGetB(col2) / 2) & 0x0ff;
382
//#endif
383
        uint newA = (SkColorGetA(col1) + SkColorGetA(col2) / 2) & 0x0ff;
384
 
385
        return SkColorSetARGB(newA, newR, newG, newB);
386
    }
387
 
388
    if (red)
389
        return col1;
390
 
391
    if (green)
392
        return col2;
393
 
394
    return SK_ColorTRANSPARENT; // transparent pixel
395
}
396
 
486 andreas 397
Button::POSITION_t TDrawImage::calcImagePosition(int width, int height, int number, size_t index)
446 andreas 398
{
486 andreas 399
    DECL_TRACER("TDrawImage::calcImagePosition(int with, int height, CENTER_CODE code, int number, size_t index)");
446 andreas 400
 
401
    Button::SR_T act_sr;
402
    Button::POSITION_t position;
403
    int ix, iy;
404
 
405
    if (mSr.size() == 0)
486 andreas 406
    {
407
        MSG_WARNING("Found no stage element! Can't calculate position.");
446 andreas 408
        return position;
486 andreas 409
    }
446 andreas 410
 
411
    if (number <= 0)
412
        act_sr = mSr.at(0);
413
    else if ((size_t)number < mSr.size())
414
        act_sr = mSr.at(number);
415
    else if ((size_t)number >= mSr.size())
416
        act_sr = mSr.at(mSr.size() - 1);
417
    else
418
        return position;
419
 
420
    int border_size = mBorderSize;
421
    int code, border = border_size;
422
    string dbgCC;
423
    int rwt = 0, rht = 0;
424
 
486 andreas 425
    if (TTPInit::isTP5())
426
    {
427
        if (index < act_sr.bitmaps.size())
428
        {
429
            code = act_sr.bitmaps[index].justification;
430
            ix = act_sr.bitmaps[index].offsetX;
431
            iy = act_sr.bitmaps[index].offsetY;
432
        }
433
        else
434
        {
435
            code = 5;
436
            ix = iy = 0;
437
        }
438
    }
439
    else
440
    {
441
        code = act_sr.jb;
442
        ix = act_sr.bx;
443
        iy = act_sr.by;
444
    }
445
 
446 andreas 446
    rwt = std::min(mWidth - border * 2, width);
447
    rht = std::min(mHeight - border_size * 2, height);
448
 
449
    if (width > rwt || height > rht)
450
        position.overflow = true;
451
 
452
    switch (code)
453
    {
454
        case 0: // absolute position
455
            position.left = ix;
456
            position.top = iy;
457
 
458
            if (ix < 0 && rwt < width)
459
                position.left *= -1;
460
 
461
            position.width = rwt;
462
            position.height = rht;
463
        break;
464
 
465
        case 1: // top, left
466
            position.width = rwt;
467
            position.height = rht;
468
        break;
469
 
470
        case 2: // center, top
471
            position.left = (mWidth - rwt) / 2;
472
            position.height = rht;
473
            position.width = rwt;
474
        break;
475
 
476
        case 3: // right, top
477
            position.left = mWidth - rwt;
478
            position.width = rwt;
479
            position.height = rht;
480
            break;
481
 
482
        case 4: // left, middle
483
            position.top = (mHeight - rht) / 2;
484
            position.width = rwt;
485
            position.height = rht;
486
            break;
487
 
488
        case 6: // right, middle
489
            position.left = mWidth - rwt;
490
            position.top = (mHeight - rht) / 2;
491
            position.width = rwt;
492
            position.height = rht;
493
            break;
494
 
495
        case 7: // left, bottom
496
            position.top = mHeight - rht;
497
            position.width = rwt;
498
            position.height = rht;
499
            break;
500
 
501
        case 8: // center, bottom
502
            position.left = (mWidth - rwt) / 2;
503
            position.top = mHeight - rht;
504
            position.width = rwt;
505
            position.height = rht;
506
            break;
507
 
508
        case 9: // right, bottom
509
            position.left = mWidth - rwt;
510
            position.top = mHeight - rht;
511
            break;
512
 
513
        default: // center, middle
514
            position.left = (mWidth - rwt) / 2;
515
            position.top = (mHeight - rht) / 2;
516
            position.width = rwt;
517
            position.height = rht;
518
    }
519
 
520
    MSG_DEBUG("PosType=" << code << ", Position: x=" << position.left << ", y=" << position.top << ", w=" << position.width << ", h=" << position.height << ", Overflow: " << (position.overflow ? "YES" : "NO"));
521
    position.valid = true;
522
    return position;
523
}