/src/libreoffice/vcl/source/bitmap/alpha.cxx
Line | Count | Source (jump to first uncovered line) |
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 <config_features.h> |
21 | | |
22 | | #include <tools/color.hxx> |
23 | | #include <vcl/alpha.hxx> |
24 | | |
25 | | #include <vcl/BitmapWriteAccess.hxx> |
26 | | #include <salinst.hxx> |
27 | | #include <svdata.hxx> |
28 | | #include <salbmp.hxx> |
29 | | #include <sal/log.hxx> |
30 | | #if HAVE_FEATURE_SKIA |
31 | | #include <vcl/skia/SkiaHelper.hxx> |
32 | | #endif |
33 | | |
34 | | |
35 | 11.3M | AlphaMask::AlphaMask() = default; |
36 | | |
37 | | AlphaMask::AlphaMask( const Bitmap& rBitmap ) : |
38 | 4.28k | maBitmap( rBitmap ) |
39 | 4.28k | { |
40 | 4.28k | if ( !rBitmap.IsEmpty() ) |
41 | 4.28k | maBitmap.Convert( BmpConversion::N8BitNoConversion ); |
42 | | #if HAVE_FEATURE_SKIA |
43 | | // Related tdf#156866 force snapshot of alpha mask when using Skia |
44 | | // In release builds, tdf#156629 and tdf#156630 reappear in many |
45 | | // cases because a BitmapInfoAccess is in a debug block. So, instead |
46 | | // of relying on other code to a create a BitmapInfoAccess instance, |
47 | | // create one here to force the alpha mask to handle any pending |
48 | | // scaling and make the alpha mask immutable. |
49 | | else if ( SkiaHelper::isVCLSkiaEnabled() ) |
50 | | BitmapInfoAccess aInfoAccess( maBitmap ); |
51 | | #endif |
52 | 4.28k | assert( (IsEmpty() || maBitmap.getPixelFormat() == vcl::PixelFormat::N8_BPP) && "alpha bitmap should be 8bpp" ); |
53 | 4.28k | assert( (IsEmpty() || maBitmap.HasGreyPalette8Bit()) && "alpha bitmap should have greyscale palette" ); |
54 | 4.28k | } |
55 | | |
56 | 471k | AlphaMask::AlphaMask( const AlphaMask& ) = default; |
57 | | |
58 | 1.46k | AlphaMask::AlphaMask( AlphaMask&& ) = default; |
59 | | |
60 | | AlphaMask::AlphaMask( const Size& rSizePixel, const sal_uInt8* pEraseTransparency ) |
61 | 25.2k | : maBitmap(rSizePixel, vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256)) |
62 | 25.2k | { |
63 | 25.2k | if( pEraseTransparency ) |
64 | 1.79k | { |
65 | 1.79k | sal_uInt8 nAlpha = 255 - *pEraseTransparency; |
66 | 1.79k | maBitmap.Erase( Color( nAlpha, nAlpha, nAlpha ) ); |
67 | 1.79k | } |
68 | 23.4k | else |
69 | 23.4k | maBitmap.Erase( COL_ALPHA_OPAQUE ); |
70 | 25.2k | } |
71 | | |
72 | 11.7M | AlphaMask::~AlphaMask() = default; |
73 | | |
74 | | AlphaMask& AlphaMask::operator=( const Bitmap& rBitmap ) |
75 | 24.8k | { |
76 | 24.8k | maBitmap = rBitmap; |
77 | | |
78 | 24.8k | if( !rBitmap.IsEmpty() ) |
79 | 24.8k | maBitmap.Convert( BmpConversion::N8BitNoConversion ); |
80 | | |
81 | 24.8k | assert( maBitmap.getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); |
82 | 24.8k | assert( maBitmap.HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); |
83 | | |
84 | 24.8k | return *this; |
85 | 24.8k | } |
86 | | |
87 | | void AlphaMask::Erase( sal_uInt8 cTransparency ) |
88 | 0 | { |
89 | 0 | sal_uInt8 nAlpha = 255 - cTransparency; |
90 | 0 | maBitmap.Erase( Color( nAlpha, nAlpha, nAlpha ) ); |
91 | 0 | } |
92 | | |
93 | | void AlphaMask::BlendWith(const AlphaMask& rOther) |
94 | 4 | { |
95 | 4 | std::shared_ptr<SalBitmap> xImpBmp(ImplGetSVData()->mpDefInst->CreateSalBitmap()); |
96 | 4 | if (xImpBmp->Create(*maBitmap.ImplGetSalBitmap()) && xImpBmp->AlphaBlendWith(*rOther.maBitmap.ImplGetSalBitmap())) |
97 | 0 | { |
98 | 0 | maBitmap.ImplSetSalBitmap(xImpBmp); |
99 | 0 | assert( maBitmap.getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); |
100 | 0 | assert( maBitmap.HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); |
101 | 0 | return; |
102 | 0 | } |
103 | 4 | BitmapScopedReadAccess pOtherAcc(rOther); |
104 | 4 | BitmapScopedWriteAccess pAcc(*this); |
105 | 4 | assert (pOtherAcc && pAcc && pOtherAcc->GetBitCount() == 8 && pAcc->GetBitCount() == 8 && "cannot BlendWith this combination"); |
106 | 4 | if (!(pOtherAcc && pAcc && pOtherAcc->GetBitCount() == 8 && pAcc->GetBitCount() == 8)) |
107 | 0 | { |
108 | 0 | SAL_WARN("vcl", "cannot BlendWith this combination"); |
109 | 0 | return; |
110 | 0 | } |
111 | | |
112 | 4 | const tools::Long nHeight = std::min(pOtherAcc->Height(), pAcc->Height()); |
113 | 4 | const tools::Long nWidth = std::min(pOtherAcc->Width(), pAcc->Width()); |
114 | 8 | for (tools::Long y = 0; y < nHeight; ++y) |
115 | 4 | { |
116 | 4 | Scanline scanline = pAcc->GetScanline( y ); |
117 | 4 | ConstScanline otherScanline = pOtherAcc->GetScanline( y ); |
118 | 8 | for (tools::Long x = 0; x < nWidth; ++x) |
119 | 4 | { |
120 | | // Use sal_uInt16 for following multiplication |
121 | 4 | const sal_uInt16 nGrey1 = *scanline; |
122 | 4 | const sal_uInt16 nGrey2 = *otherScanline; |
123 | | // Awkward calculation because the original used transparency, and to replicate |
124 | | // the logic we need to translate into transparency, perform the original logic, |
125 | | // then translate back to alpha. |
126 | | // The original looked like: |
127 | | // auto tmp = nGrey1 + nGrey2 - (nGrey1 * nGrey2 / 255) |
128 | | // which, when converted to using alpha looks like |
129 | | // auto tmp = 255 - ((255 - nGrey1) + (255 - nGrey2) - (255 - nGrey1) * (255 - nGrey2) / 255); |
130 | | // which then simplifies to: |
131 | 4 | auto tmp = nGrey1 * nGrey2 / 255; |
132 | 4 | *scanline = static_cast<sal_uInt8>(tmp); |
133 | 4 | ++scanline; |
134 | 4 | ++otherScanline; |
135 | 4 | } |
136 | 4 | } |
137 | 4 | pAcc.reset(); |
138 | 4 | assert( maBitmap.getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); |
139 | 4 | assert( maBitmap.HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); |
140 | 4 | } |
141 | | |
142 | | bool AlphaMask::hasAlpha() const |
143 | 0 | { |
144 | | // no content, no alpha |
145 | 0 | if(IsEmpty()) |
146 | 0 | return false; |
147 | | |
148 | 0 | BitmapScopedReadAccess pAcc(*this); |
149 | 0 | const tools::Long nHeight(pAcc->Height()); |
150 | 0 | const tools::Long nWidth(pAcc->Width()); |
151 | | |
152 | | // no content, no alpha |
153 | 0 | if(0 == nHeight || 0 == nWidth) |
154 | 0 | return false; |
155 | | |
156 | 0 | for (tools::Long y = 0; y < nHeight; ++y) |
157 | 0 | { |
158 | 0 | for (tools::Long x = 0; x < nWidth; ++x) |
159 | 0 | { |
160 | 0 | if (255 != pAcc->GetColor(y, x).GetRed()) |
161 | 0 | { |
162 | 0 | return true; |
163 | 0 | } |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | 0 | return false; |
168 | 0 | } |
169 | | |
170 | | bool AlphaMask::AlphaCombineOr(const AlphaMask& rMask) |
171 | 0 | { |
172 | 0 | BitmapScopedReadAccess pMaskAcc(rMask); |
173 | 0 | BitmapScopedWriteAccess pAcc(*this); |
174 | |
|
175 | 0 | if (!pMaskAcc || !pAcc) |
176 | 0 | return false; |
177 | | |
178 | 0 | assert (pMaskAcc->GetBitCount() == 8 && pAcc->GetBitCount() == 8); |
179 | |
|
180 | 0 | const tools::Long nWidth = std::min(pMaskAcc->Width(), pAcc->Width()); |
181 | 0 | const tools::Long nHeight = std::min(pMaskAcc->Height(), pAcc->Height()); |
182 | |
|
183 | 0 | for (tools::Long nY = 0; nY < nHeight; nY++) |
184 | 0 | { |
185 | 0 | Scanline pScanline = pAcc->GetScanline(nY); |
186 | 0 | ConstScanline pScanlineMask = pMaskAcc->GetScanline(nY); |
187 | 0 | for (tools::Long nX = 0; nX < nWidth; nX++) |
188 | 0 | { |
189 | 0 | if (*pScanlineMask != 255 || *pScanline != 255) |
190 | 0 | *pScanline = 0; |
191 | 0 | else |
192 | 0 | *pScanline = 255; |
193 | 0 | ++pScanline; |
194 | 0 | ++pScanlineMask; |
195 | 0 | } |
196 | 0 | } |
197 | |
|
198 | 0 | return true; |
199 | 0 | } |
200 | | |
201 | | bool AlphaMask::Invert() |
202 | 2.24k | { |
203 | 2.24k | if (IsEmpty()) |
204 | 1.45k | return false; |
205 | 783 | bool b = maBitmap.Invert(); |
206 | 783 | assert( maBitmap.getPixelFormat() == vcl::PixelFormat::N8_BPP && "alpha bitmap should be 8bpp" ); |
207 | 783 | assert( maBitmap.HasGreyPalette8Bit() && "alpha bitmap should have greyscale palette" ); |
208 | 783 | return b; |
209 | 2.24k | } |
210 | | |
211 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |