• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 15240502558

25 May 2025 05:54PM UTC coverage: 50.453% (+0.2%) from 50.286%
15240502558

Pull #1536

github

web-flow
Merge 56874c299 into 0d2a837ba
Pull Request #1536: Add support for image cropping inside ctrlImageButton

55 of 64 new or added lines in 6 files covered. (85.94%)

4 existing lines in 4 files now uncovered.

22452 of 44501 relevant lines covered (50.45%)

34399.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.57
/libs/s25main/ogl/glSmartBitmap.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "glSmartBitmap.h"
6
#include "Loader.h"
7
#include "drivers/VideoDriverWrapper.h"
8
#include "helpers/mathFuncs.h"
9
#include "ogl/glBitmapItem.h"
10
#include "libsiedler2/ArchivItem_Bitmap.h"
11
#include "libsiedler2/ArchivItem_Bitmap_Player.h"
12
#include "libsiedler2/PixelBufferBGRA.h"
13
#include "s25util/colors.h"
14
#include <glad/glad.h>
15
#include <cmath>
16
#include <limits>
17

18
namespace {
19
struct GL_RGBAColor
20
{
21
    GLbyte r, g, b, a;
22
};
23
} // namespace
24

25
glSmartBitmap::glSmartBitmap() : origin_(0, 0), size_(0, 0), sharedTexture(false), texture(0), hasPlayer(false) {}
76,404✔
26

27
glSmartBitmap::~glSmartBitmap()
76,404✔
28
{
29
    reset();
76,404✔
30
}
76,404✔
31

32
void glSmartBitmap::reset()
76,404✔
33
{
34
    if(texture && !sharedTexture)
76,404✔
35
        VIDEODRIVER.DeleteTexture(texture);
2✔
36
    texture = 0;
76,404✔
37

38
    for(glBitmapItem& bmpItem : items)
76,417✔
39
    {
40
        if(bmpItem.isOwning_)
13✔
41
            delete bmpItem.bmp;
×
42
    }
43
    items.clear();
76,404✔
44
}
76,404✔
45

46
Extent glSmartBitmap::getRequiredTexSize() const
24✔
47
{
48
    Extent texSize(size_);
24✔
49
    if(hasPlayer)
24✔
50
        texSize.x *= 2;
2✔
51
    return texSize;
24✔
52
}
53

54
void glSmartBitmap::add(libsiedler2::ArchivItem_Bitmap_Player* bmp)
4✔
55
{
56
    if(!bmp)
4✔
57
        return;
×
58
    items.push_back(glBitmapItem(bmp));
4✔
59
    calcDimensions();
4✔
60
}
61

62
void glSmartBitmap::add(libsiedler2::baseArchivItem_Bitmap* bmp)
9✔
63
{
64
    if(!bmp)
9✔
65
        return;
×
66
    items.push_back(glBitmapItem(bmp));
9✔
67
    calcDimensions();
9✔
68
}
69

70
void glSmartBitmap::add(std::unique_ptr<libsiedler2::baseArchivItem_Bitmap> bmp)
×
71
{
72
    if(!bmp)
×
73
        return;
×
74
    items.push_back(glBitmapItem(bmp.release(), false, true));
×
75
    calcDimensions();
×
76
}
77

78
void glSmartBitmap::addShadow(libsiedler2::baseArchivItem_Bitmap* bmp)
×
79
{
80
    if(!bmp)
×
81
        return;
×
82
    items.push_back(glBitmapItem(bmp, true));
×
83
    calcDimensions();
×
84
}
85

86
void glSmartBitmap::calcDimensions()
13✔
87
{
88
    if(items.empty())
13✔
89
    {
90
        origin_ = Position(0, 0);
×
91
        size_ = Extent(0, 0);
×
92
        return;
×
93
    }
94

95
    origin_.x = origin_.y = std::numeric_limits<int>::min();
13✔
96
    Position maxPos = origin_;
13✔
97

98
    hasPlayer = false;
13✔
99

100
    for(const glBitmapItem& bmpItem : items)
32✔
101
    {
102
        if(bmpItem.type == glBitmapItemType::PlayerBitmap)
19✔
103
            hasPlayer = true;
7✔
104

105
        origin_ = elMax(origin_, bmpItem.origin);
19✔
106
        maxPos = elMax(maxPos, bmpItem.size - bmpItem.origin);
19✔
107
    }
108

109
    size_ = Extent(origin_ + maxPos);
13✔
110
}
111

112
void glSmartBitmap::drawTo(libsiedler2::PixelBufferBGRA& buffer, const Extent& bufOffset) const
9✔
113
{
114
    libsiedler2::ArchivItem_Palette* p_colors = LOADER.GetPaletteN("colors");
18✔
115
    libsiedler2::ArchivItem_Palette* p_5 = LOADER.GetPaletteN("pal5");
18✔
116

117
    for(const glBitmapItem& bmpItem : items)
22✔
118
    {
119
        if((bmpItem.size.x == 0) || (bmpItem.size.y == 0))
13✔
120
            continue;
×
121

122
        DrawPoint offset = origin_ - bmpItem.origin;
13✔
123

124
        if(bmpItem.type == glBitmapItemType::ShadowBitmap)
13✔
125
        {
126
            libsiedler2::PixelBufferBGRA tmp(size_.x, size_.y);
×
127

128
            dynamic_cast<libsiedler2::baseArchivItem_Bitmap*>(bmpItem.bmp) //-V522
×
129
              ->print(tmp, p_5, offset.x, offset.y, bmpItem.pos.x, bmpItem.pos.y, bmpItem.size.x, bmpItem.size.y);
×
130

131
            unsigned tmpIdx = 0;
×
132
            for(unsigned y = 0; y < size_.y; ++y)
×
133
            {
134
                unsigned idx = buffer.calcIdx(bufOffset.x, bufOffset.y + y);
×
135
                for(unsigned x = 0; x < size_.x; ++x)
×
136
                {
137
                    if(tmp.get(tmpIdx).getAlpha() != 0x00 && buffer.get(idx).getAlpha() == 0x00)
×
138
                        buffer.set(idx, libsiedler2::ColorBGRA(0, 0, 0, 0x40));
×
139
                    idx++;
×
140
                    tmpIdx++;
×
141
                }
142
            }
143
        } else if(!hasPlayer)
13✔
144
        {
145
            // No player bitmap -> Just (over)write the data
146
            RTTR_Assert(bmpItem.type == glBitmapItemType::Normal);
9✔
147
            dynamic_cast<libsiedler2::baseArchivItem_Bitmap*>(bmpItem.bmp)
9✔
148
              ->print(buffer, p_5, offset.x + bufOffset.x, offset.y + bufOffset.y, bmpItem.pos.x, bmpItem.pos.y,
9✔
149
                      bmpItem.size.x, bmpItem.size.y);
9✔
150
        } else
151
        {
152
            // There is a player bitmap -> First write to temp buffer
153
            libsiedler2::PixelBufferBGRA tmp(size_.x, size_.y);
20✔
154
            if(bmpItem.type == glBitmapItemType::Normal)
4✔
155
            {
156
                dynamic_cast<libsiedler2::baseArchivItem_Bitmap*>(bmpItem.bmp)
×
157
                  ->print(tmp, p_5, offset.x, offset.y, bmpItem.pos.x, bmpItem.pos.y, bmpItem.size.x, bmpItem.size.y);
×
158
            } else
159
            {
160
                dynamic_cast<libsiedler2::ArchivItem_Bitmap_Player*>(bmpItem.bmp)
4✔
161
                  ->print(tmp, p_colors, 128, offset.x, offset.y, bmpItem.pos.x, bmpItem.pos.y, bmpItem.size.x,
4✔
162
                          bmpItem.size.y, false);
4✔
163
            }
164
            // Now copy temp buffer to real buffer, but we need to reset all player colors that would be overwritten
165
            // so it looks like, the first bitmap is fully drawn (including player colors) and then the next
166
            // overwrites it
167
            unsigned tmpIdx = 0;
4✔
168
            for(unsigned y = 0; y < size_.y; ++y)
356✔
169
            {
170
                unsigned idx = buffer.calcIdx(bufOffset.x + offset.x, bufOffset.y + y);
352✔
171
                tmpIdx += offset.x;
352✔
172
                for(unsigned x = offset.x; x < size_.x; x++)
24,336✔
173
                {
174
                    // Check for non-transparent pixels
175
                    if(tmp.get(tmpIdx).getAlpha())
23,984✔
176
                    {
177
                        // Copy to buffer
178
                        buffer.set(idx, tmp.get(tmpIdx));
2,426✔
179
                        // Reset player color to transparent
180
                        buffer.set(idx + size_.x, libsiedler2::ColorBGRA());
9,704✔
181
                    }
182
                    ++idx;
23,984✔
183
                    ++tmpIdx;
23,984✔
184
                }
185
            }
186
            // Finally write the player color part if it has one
187
            if(bmpItem.type == glBitmapItemType::PlayerBitmap)
4✔
188
            {
189
                dynamic_cast<libsiedler2::ArchivItem_Bitmap_Player*>(bmpItem.bmp)
4✔
190
                  ->print(buffer, p_colors, 128, offset.x + size_.x + bufOffset.x, offset.y + bufOffset.y,
4✔
191
                          bmpItem.pos.x, bmpItem.pos.y, bmpItem.size.x, bmpItem.size.y, true);
4✔
192
            }
193
        }
194
    }
195
}
9✔
196

197
void glSmartBitmap::generateTexture()
2✔
198
{
199
    if(items.empty())
2✔
200
        return;
×
201

202
    if(!texture)
2✔
203
    {
204
        texture = VIDEODRIVER.GenerateTexture();
2✔
205

206
        if(!texture)
2✔
207
            return;
×
208
    }
209

210
    const Extent bufSize = VIDEODRIVER.calcPreferredTextureSize(getRequiredTexSize());
2✔
211

212
    libsiedler2::PixelBufferBGRA buffer(bufSize.x, bufSize.y);
10✔
213
    drawTo(buffer);
2✔
214

215
    VIDEODRIVER.BindTexture(texture);
2✔
216

217
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2✔
218
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2✔
219

220
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufSize.x, bufSize.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer.getPixelPtr());
2✔
221

222
    /* 0--3/4--7
223
     * |  |    |
224
     * 1--2/5--6
225
     **/
226
    texCoords[0] = PointF::all(0);
2✔
227
    texCoords[2] = PointF(getRequiredTexSize()) / PointF(bufSize);
2✔
228
    if(hasPlayer)
2✔
229
    {
230
        texCoords[6] = texCoords[2];
×
231
        texCoords[2].x /= 2.f;
×
232
    }
233
    texCoords[1] = PointF(texCoords[0].x, texCoords[2].y);
2✔
234
    texCoords[3] = PointF(texCoords[2].x, texCoords[0].y);
2✔
235
    if(hasPlayer)
2✔
236
    {
237
        texCoords[4] = texCoords[3];
×
238
        texCoords[5] = texCoords[2];
×
239
        texCoords[7] = PointF(texCoords[6].x, texCoords[4].y);
×
240
    }
241
}
242

NEW
243
void glSmartBitmap::Draw(Rect dstArea, Rect srcArea, unsigned color /*= 0xFFFFFFFF*/)
×
244
{
NEW
245
    drawRect(dstArea, srcArea, color);
×
NEW
246
}
×
247

UNCOV
248
void glSmartBitmap::draw(DrawPoint drawPt, unsigned color, unsigned player_color)
×
249
{
250
    drawPercent(drawPt, 100, color, player_color);
×
251
}
×
252

253
void glSmartBitmap::drawPercent(DrawPoint drawPt, unsigned percent, unsigned color, unsigned player_color)
6✔
254
{
255
    // nothing to draw?
256
    if(!percent)
6✔
257
        return;
2✔
258
    RTTR_Assert(percent <= 100);
4✔
259

260
    const float partDrawn = percent / 100.f;
4✔
261
    auto startY = int(std::round(size_.y * (1 - partDrawn)));
4✔
262
    auto height = size_.y - startY;
4✔
263
    Rect dstArea(drawPt.x, drawPt.y + startY, size_.x, height);
4✔
264
    Rect srcArea(0, startY, size_.x, height);
4✔
265
    drawRect(dstArea, srcArea, color, player_color);
4✔
266
}
267

268
void glSmartBitmap::drawRect(Rect dstArea, Rect srcArea, unsigned color /*= 0xFFFFFFFF*/, unsigned player_color /*= 0*/)
4✔
269
{
270
    if(!texture)
4✔
271
    {
272
        generateTexture();
2✔
273

274
        if(!texture)
2✔
275
            return;
×
276
    }
277

278
    std::array<Point<GLfloat>, 8> vertices, curTexCoords;
52✔
279
    std::array<GL_RGBAColor, 8> colors;
280

281
    auto drawPt = dstArea.getOrigin();
4✔
282
    drawPt -= origin_;
4✔
283
    vertices[2] = Point<GLfloat>(dstArea.getEndPt() - origin_); // destination bottom
4✔
284

285
    vertices[0].x = vertices[1].x = GLfloat(drawPt.x);
4✔
286
    vertices[3].x = vertices[2].x;
4✔
287

288
    vertices[0].y = vertices[3].y = GLfloat(drawPt.y); // destination top
4✔
289
    vertices[1].y = vertices[2].y;                     // destination bottom
4✔
290

291
    colors[0].r = GetRed(color);
4✔
292
    colors[0].g = GetGreen(color);
4✔
293
    colors[0].b = GetBlue(color);
4✔
294
    colors[0].a = GetAlpha(color);
4✔
295
    colors[3] = colors[2] = colors[1] = colors[0];
4✔
296

297
    //  0--3/4--7
298
    //  |   |   |
299
    //  1--2/5--6
300
    // Remap srcArea to texture coords
301
    curTexCoords[0].x = helpers::lerp(texCoords[0].x, texCoords[3].x, srcArea.getOrigin().x / float(size_.x));
4✔
302
    curTexCoords[0].y = helpers::lerp(texCoords[0].y, texCoords[1].y, srcArea.getOrigin().y / float(size_.y));
4✔
303
    curTexCoords[2].x = helpers::lerp(texCoords[0].x, texCoords[3].x, srcArea.getEndPt().x / float(size_.x));
4✔
304
    curTexCoords[2].y = helpers::lerp(texCoords[0].y, texCoords[1].y, srcArea.getEndPt().y / float(size_.y));
4✔
305

306
    curTexCoords[1] = PointF(curTexCoords[0].x, curTexCoords[2].y);
4✔
307
    curTexCoords[3] = PointF(curTexCoords[2].x, curTexCoords[0].y);
4✔
308

309
    int numQuads;
310
    if(player_color && hasPlayer)
4✔
311
    {
312
        std::copy(vertices.begin(), vertices.begin() + 4, vertices.begin() + 4);
×
313

314
        colors[4].r = GetRed(player_color);
×
315
        colors[4].g = GetGreen(player_color);
×
316
        colors[4].b = GetBlue(player_color);
×
317
        colors[4].a = GetAlpha(player_color);
×
318
        colors[7] = colors[6] = colors[5] = colors[4];
×
319

320
        curTexCoords[4] = texCoords[4];
×
321
        curTexCoords[5] = texCoords[5];
×
322
        curTexCoords[6] = texCoords[6];
×
323
        curTexCoords[7] = texCoords[7];
×
324
        curTexCoords[4].y = curTexCoords[7].y = curTexCoords[0].y;
×
325

326
        numQuads = 8;
×
327
    } else
328
        numQuads = 4;
4✔
329

330
    glEnableClientState(GL_COLOR_ARRAY);
4✔
331
    glVertexPointer(2, GL_FLOAT, 0, vertices.data());
4✔
332
    glTexCoordPointer(2, GL_FLOAT, 0, curTexCoords.data());
4✔
333
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors.data());
4✔
334
    VIDEODRIVER.BindTexture(texture);
4✔
335
    glDrawArrays(GL_QUADS, 0, numQuads);
4✔
336
    glDisableClientState(GL_COLOR_ARRAY);
4✔
337
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc