Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

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