Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/bitmap/Octree.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <vcl/BitmapReadAccess.hxx>
21
22
#include <bitmap/Octree.hxx>
23
24
namespace
25
{
26
constexpr size_t OCTREE_BITS = 5;
27
constexpr size_t OCTREE_BITS_1 = 10;
28
29
constexpr sal_uLong gnBits = 8 - OCTREE_BITS;
30
31
} // end anonymous namespace
32
33
Octree::Octree(const BitmapReadAccess& rReadAcc, sal_uLong nColors)
34
1.32k
    : mnLeafCount(0)
35
1.32k
    , mnLevel(0)
36
1.32k
    , mpReduce(OCTREE_BITS + 1, nullptr)
37
1.32k
    , mnPalIndex(0)
38
1.32k
{
39
1.32k
    const BitmapReadAccess* pAccess = &rReadAcc;
40
1.32k
    sal_uLong nMax(nColors);
41
42
1.32k
    if (!*pAccess)
43
0
        return;
44
45
1.32k
    const tools::Long nWidth = pAccess->Width();
46
1.32k
    const tools::Long nHeight = pAccess->Height();
47
48
1.32k
    if (pAccess->HasPalette())
49
0
    {
50
0
        for (tools::Long nY = 0; nY < nHeight; nY++)
51
0
        {
52
0
            Scanline pScanline = pAccess->GetScanline(nY);
53
0
            for (tools::Long nX = 0; nX < nWidth; nX++)
54
0
            {
55
0
                mnLevel = 0;
56
0
                add(pTree, pAccess->GetPaletteColor(pAccess->GetIndexFromData(pScanline, nX)));
57
58
0
                while (mnLeafCount > nMax)
59
0
                    reduce();
60
0
            }
61
0
        }
62
0
    }
63
1.32k
    else
64
1.32k
    {
65
222k
        for (tools::Long nY = 0; nY < nHeight; nY++)
66
220k
        {
67
220k
            Scanline pScanline = pAccess->GetScanline(nY);
68
32.5M
            for (tools::Long nX = 0; nX < nWidth; nX++)
69
32.3M
            {
70
32.3M
                mnLevel = 0;
71
32.3M
                add(pTree, pAccess->GetPixelFromData(pScanline, nX));
72
73
32.4M
                while (mnLeafCount > nMax)
74
39.3k
                    reduce();
75
32.3M
            }
76
220k
        }
77
1.32k
    }
78
1.32k
}
79
80
1.32k
Octree::~Octree() {}
81
82
void Octree::add(std::unique_ptr<OctreeNode>& rpNode, BitmapColor const& color)
83
193M
{
84
    // possibly generate new nodes
85
193M
    if (!rpNode)
86
522k
    {
87
522k
        rpNode.reset(new OctreeNode);
88
522k
        rpNode->bLeaf = (OCTREE_BITS == mnLevel);
89
90
522k
        if (rpNode->bLeaf)
91
322k
            mnLeafCount++;
92
199k
        else
93
199k
        {
94
199k
            rpNode->pNext = mpReduce[mnLevel];
95
199k
            mpReduce[mnLevel] = rpNode.get();
96
199k
        }
97
522k
    }
98
99
193M
    if (rpNode->bLeaf)
100
32.3M
    {
101
32.3M
        rpNode->nCount++;
102
32.3M
        rpNode->nRed += color.GetRed();
103
32.3M
        rpNode->nGreen += color.GetGreen();
104
32.3M
        rpNode->nBlue += color.GetBlue();
105
32.3M
    }
106
161M
    else
107
161M
    {
108
161M
        const sal_uLong nShift = 7 - mnLevel;
109
161M
        const sal_uInt8 cMask = 0x80 >> mnLevel;
110
161M
        const sal_uLong nIndex = (((color.GetRed() & cMask) >> nShift) << 2)
111
161M
                                 | (((color.GetGreen() & cMask) >> nShift) << 1)
112
161M
                                 | ((color.GetBlue() & cMask) >> nShift);
113
114
161M
        mnLevel++;
115
161M
        add(rpNode->pChild[nIndex], color);
116
161M
    }
117
193M
}
118
119
void Octree::reduce()
120
39.3k
{
121
39.3k
    OctreeNode* pNode;
122
39.3k
    sal_uLong nRedSum = 0;
123
39.3k
    sal_uLong nGreenSum = 0;
124
39.3k
    sal_uLong nBlueSum = 0;
125
39.3k
    sal_uLong nChildren = 0;
126
127
39.3k
    sal_uLong nIndex = OCTREE_BITS - 1;
128
39.5k
    while (nIndex > 0 && !mpReduce[nIndex])
129
182
    {
130
182
        nIndex--;
131
182
    }
132
133
39.3k
    pNode = mpReduce[nIndex];
134
39.3k
    mpReduce[nIndex] = pNode->pNext;
135
136
353k
    for (unsigned int i = 0; i < 8; i++)
137
314k
    {
138
314k
        if (pNode->pChild[i])
139
84.6k
        {
140
84.6k
            OctreeNode* pChild = pNode->pChild[i].get();
141
142
84.6k
            nRedSum += pChild->nRed;
143
84.6k
            nGreenSum += pChild->nGreen;
144
84.6k
            nBlueSum += pChild->nBlue;
145
84.6k
            pNode->nCount += pChild->nCount;
146
147
84.6k
            pNode->pChild[i].reset();
148
84.6k
            nChildren++;
149
84.6k
        }
150
314k
    }
151
152
39.3k
    pNode->bLeaf = true;
153
39.3k
    pNode->nRed = nRedSum;
154
39.3k
    pNode->nGreen = nGreenSum;
155
39.3k
    pNode->nBlue = nBlueSum;
156
39.3k
    mnLeafCount -= --nChildren;
157
39.3k
}
158
159
void Octree::CreatePalette(OctreeNode* pNode)
160
437k
{
161
437k
    if (pNode->bLeaf)
162
277k
    {
163
277k
        pNode->nPalIndex = mnPalIndex;
164
277k
        maPalette[mnPalIndex++] = BitmapColor(sal_uInt8(double(pNode->nRed) / pNode->nCount),
165
277k
                                              sal_uInt8(double(pNode->nGreen) / pNode->nCount),
166
277k
                                              sal_uInt8(double(pNode->nBlue) / pNode->nCount));
167
277k
    }
168
160k
    else
169
160k
    {
170
160k
        for (auto const& i : pNode->pChild)
171
1.28M
        {
172
1.28M
            if (i)
173
436k
            {
174
436k
                CreatePalette(i.get());
175
436k
            }
176
1.28M
        }
177
160k
    }
178
437k
}
179
180
void Octree::GetPalIndex(const OctreeNode* pNode, BitmapColor const& color)
181
0
{
182
0
    if (pNode->bLeaf)
183
0
        mnPalIndex = pNode->nPalIndex;
184
0
    else
185
0
    {
186
0
        const sal_uLong nShift = 7 - mnLevel;
187
0
        const sal_uInt8 cMask = 0x80 >> mnLevel;
188
0
        mnLevel++;
189
0
        const sal_uLong nIndex = (((color.GetRed() & cMask) >> nShift) << 2)
190
0
                                 | (((color.GetGreen() & cMask) >> nShift) << 1)
191
0
                                 | ((color.GetBlue() & cMask) >> nShift);
192
193
0
        GetPalIndex(pNode->pChild[nIndex].get(), color);
194
0
    }
195
0
}
196
197
const BitmapPalette& Octree::GetPalette()
198
1.32k
{
199
1.32k
    maPalette.SetEntryCount(sal_uInt16(mnLeafCount));
200
1.32k
    mnPalIndex = 0;
201
1.32k
    CreatePalette(pTree.get());
202
1.32k
    return maPalette;
203
1.32k
}
204
205
sal_uInt16 Octree::GetBestPaletteIndex(const BitmapColor& rColor)
206
0
{
207
0
    mnPalIndex = 65535;
208
0
    mnLevel = 0;
209
0
    GetPalIndex(pTree.get(), rColor);
210
0
    return mnPalIndex;
211
0
}
212
213
constexpr int nColorMax = 1 << OCTREE_BITS;
214
215
InverseColorMap::InverseColorMap(const BitmapPalette& rPal)
216
1.32k
{
217
1.32k
    const unsigned long xsqr = 1 << (gnBits << 1);
218
1.32k
    const unsigned long xsqr2 = xsqr << 1;
219
1.32k
    const int nColors = rPal.GetEntryCount();
220
1.32k
    const tools::Long x = 1 << gnBits;
221
1.32k
    const tools::Long x2 = x >> 1;
222
1.32k
    sal_uLong r, g, b;
223
1.32k
    tools::Long rxx, gxx, bxx;
224
225
1.32k
    ImplCreateBuffers();
226
227
278k
    for (int nIndex = 0; nIndex < nColors; nIndex++)
228
277k
    {
229
277k
        const BitmapColor& rColor = rPal[static_cast<sal_uInt16>(nIndex)];
230
277k
        const tools::Long cRed = rColor.GetRed();
231
277k
        const tools::Long cGreen = rColor.GetGreen();
232
277k
        const tools::Long cBlue = rColor.GetBlue();
233
234
277k
        tools::Long rdist = cRed - x2;
235
277k
        tools::Long gdist = cGreen - x2;
236
277k
        tools::Long bdist = cBlue - x2;
237
277k
        rdist = rdist * rdist + gdist * gdist + bdist * bdist;
238
239
277k
        const tools::Long crinc = (xsqr - (cRed << gnBits)) << 1;
240
277k
        const tools::Long cginc = (xsqr - (cGreen << gnBits)) << 1;
241
277k
        const tools::Long cbinc = (xsqr - (cBlue << gnBits)) << 1;
242
243
277k
        sal_uLong* cdp = reinterpret_cast<sal_uLong*>(mpBuffer.data());
244
277k
        sal_uInt8* crgbp = mpMap.data();
245
246
9.14M
        for (r = 0, rxx = crinc; r < nColorMax; rdist += rxx, r++, rxx += xsqr2)
247
8.86M
        {
248
292M
            for (g = 0, gdist = rdist, gxx = cginc; g < nColorMax; gdist += gxx, g++, gxx += xsqr2)
249
283M
            {
250
9.36G
                for (b = 0, bdist = gdist, bxx = cbinc; b < nColorMax;
251
9.08G
                     bdist += bxx, b++, cdp++, crgbp++, bxx += xsqr2)
252
9.08G
                    if (!nIndex || static_cast<tools::Long>(*cdp) > bdist)
253
1.35G
                    {
254
1.35G
                        *cdp = bdist;
255
1.35G
                        *crgbp = static_cast<sal_uInt8>(nIndex);
256
1.35G
                    }
257
283M
            }
258
8.86M
        }
259
277k
    }
260
1.32k
}
261
262
1.32k
InverseColorMap::~InverseColorMap() {}
263
264
void InverseColorMap::ImplCreateBuffers()
265
1.32k
{
266
1.32k
    const sal_uLong nCount = nColorMax * nColorMax * nColorMax;
267
1.32k
    const sal_uLong nSize = nCount * sizeof(sal_uLong);
268
269
1.32k
    mpMap.resize(nCount, 0x00);
270
1.32k
    mpBuffer.resize(nSize, 0xff);
271
1.32k
}
272
273
sal_uInt8 InverseColorMap::GetBestPaletteIndex(const BitmapColor& rColor)
274
32.3M
{
275
32.3M
    return mpMap[((static_cast<sal_uInt32>(rColor.GetRed()) >> gnBits) << OCTREE_BITS_1)
276
32.3M
                 | ((static_cast<sal_uInt32>(rColor.GetGreen()) >> gnBits) << OCTREE_BITS)
277
32.3M
                 | (static_cast<sal_uInt32>(rColor.GetBlue()) >> gnBits)];
278
32.3M
}
279
280
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */