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