1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
11 #include <tools/helpers.hxx>
13 #include <vcl/bitmap/BitmapGaussianSeparableBlurFilter.hxx>
14 #include <vcl/BitmapWriteAccess.hxx>
16 BitmapEx
BitmapGaussianSeparableBlurFilter::execute(BitmapEx
const& rBitmapEx
) const
18 Bitmap
aBitmap(rBitmapEx
.GetBitmap());
20 const sal_Int32 nWidth
= aBitmap
.GetSizePixel().Width();
21 const sal_Int32 nHeight
= aBitmap
.GetSizePixel().Height();
23 // Prepare Blur Vector
24 int aNumberOfContributions
;
25 std::vector
<double> aBlurVector(makeBlurKernel(mfRadius
, aNumberOfContributions
));
26 std::vector
<double> aWeights
;
27 std::vector
<int> aPixels
;
28 std::vector
<int> aCounts
;
30 // Do horizontal filtering
31 blurContributions(nWidth
, aNumberOfContributions
, aBlurVector
, aWeights
, aPixels
, aCounts
);
33 BitmapScopedReadAccess
pReadAcc(aBitmap
);
35 // switch coordinates as convolution pass transposes result
36 Bitmap
aNewBitmap(Size(nHeight
, nWidth
), vcl::PixelFormat::N24_BPP
);
38 bool bResult
= convolutionPass(aBitmap
, aNewBitmap
, pReadAcc
.get(), aNumberOfContributions
,
39 aWeights
.data(), aPixels
.data(), aCounts
.data());
53 // Swap current bitmap with new bitmap
54 aBitmap
.ReassignWithSize(aNewBitmap
);
56 // Do vertical filtering
57 blurContributions(nHeight
, aNumberOfContributions
, aBlurVector
, aWeights
, aPixels
, aCounts
);
60 aNewBitmap
= Bitmap(Size(nWidth
, nHeight
), vcl::PixelFormat::N24_BPP
);
61 bResult
= convolutionPass(aBitmap
, aNewBitmap
, pReadAcc
.get(), aNumberOfContributions
,
62 aWeights
.data(), aPixels
.data(), aCounts
.data());
72 aBitmap
.ReassignWithSize(aNewBitmap
); // swap current bitmap with new bitmap
76 return BitmapEx(aBitmap
);
81 bool BitmapGaussianSeparableBlurFilter::convolutionPass(const Bitmap
& rBitmap
, Bitmap
& aNewBitmap
,
82 BitmapReadAccess
const* pReadAcc
,
83 int aNumberOfContributions
,
84 const double* pWeights
, int const* pPixels
,
90 BitmapScopedWriteAccess
pWriteAcc(aNewBitmap
);
94 const int nHeight
= rBitmap
.GetSizePixel().Height();
95 assert(rBitmap
.GetSizePixel().Height() == aNewBitmap
.GetSizePixel().Width());
96 const int nWidth
= rBitmap
.GetSizePixel().Width();
97 assert(rBitmap
.GetSizePixel().Width() == aNewBitmap
.GetSizePixel().Height());
100 double aValueRed
, aValueGreen
, aValueBlue
;
101 double aSum
, aWeight
;
102 int aBaseIndex
, aIndex
;
104 for (int nSourceY
= 0; nSourceY
< nHeight
; ++nSourceY
)
106 for (int nSourceX
= 0; nSourceX
< nWidth
; ++nSourceX
)
108 aBaseIndex
= nSourceX
* aNumberOfContributions
;
109 aSum
= aValueRed
= aValueGreen
= aValueBlue
= 0.0;
111 for (int j
= 0; j
< pCount
[nSourceX
]; ++j
)
113 aIndex
= aBaseIndex
+ j
;
114 aWeight
= pWeights
[aIndex
];
117 aColor
= pReadAcc
->GetColor(nSourceY
, pPixels
[aIndex
]);
119 aValueRed
+= aWeight
* aColor
.GetRed();
120 aValueGreen
+= aWeight
* aColor
.GetGreen();
121 aValueBlue
+= aWeight
* aColor
.GetBlue();
124 BitmapColor
aResultColor(
125 static_cast<sal_uInt8
>(std::clamp(aValueRed
/ aSum
, 0.0, 255.0)),
126 static_cast<sal_uInt8
>(std::clamp(aValueGreen
/ aSum
, 0.0, 255.0)),
127 static_cast<sal_uInt8
>(std::clamp(aValueBlue
/ aSum
, 0.0, 255.0)));
129 int nDestX
= nSourceY
;
130 int nDestY
= nSourceX
;
132 pWriteAcc
->SetPixel(nDestY
, nDestX
, aResultColor
);
138 std::vector
<double> BitmapGaussianSeparableBlurFilter::makeBlurKernel(const double radius
,
141 int intRadius
= static_cast<int>(radius
+ 1.0);
142 rows
= intRadius
* 2 + 1;
143 std::vector
<double> matrix(rows
);
145 double sigma
= radius
/ 3;
146 double radius2
= radius
* radius
;
148 for (int row
= -intRadius
; row
<= intRadius
; row
++)
150 double distance
= row
* row
;
151 if (distance
> radius2
)
157 matrix
[index
] = exp(-distance
/ (2.0 * sigma
* sigma
)) / sqrt(2.0 * M_PI
* sigma
);
164 void BitmapGaussianSeparableBlurFilter::blurContributions(
165 const int aSize
, const int aNumberOfContributions
, const std::vector
<double>& rBlurVector
,
166 std::vector
<double>& rWeights
, std::vector
<int>& rPixels
, std::vector
<int>& rCounts
)
168 rWeights
.resize(aSize
* aNumberOfContributions
);
169 rPixels
.resize(aSize
* aNumberOfContributions
);
170 rCounts
.resize(aSize
);
172 int aLeft
, aRight
, aCurrentCount
, aPixelIndex
;
175 for (int i
= 0; i
< aSize
; i
++)
177 aLeft
= i
- aNumberOfContributions
/ 2;
178 aRight
= i
+ aNumberOfContributions
/ 2;
180 for (int j
= aLeft
; j
<= aRight
; j
++)
182 aWeight
= rBlurVector
[aCurrentCount
];
191 aPixelIndex
= (aSize
- j
) + aSize
- 1;
198 // Edge case for small bitmaps
199 if (aPixelIndex
< 0 || aPixelIndex
>= aSize
)
204 rWeights
[i
* aNumberOfContributions
+ aCurrentCount
] = aWeight
;
205 rPixels
[i
* aNumberOfContributions
+ aCurrentCount
] = aPixelIndex
;
209 rCounts
[i
] = aCurrentCount
;
213 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */