/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: */ |