/src/mozilla-central/gfx/2d/DrawTarget.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "2D.h" |
8 | | #include "Logging.h" |
9 | | #include "PathHelpers.h" |
10 | | #include "Tools.h" |
11 | | |
12 | | #include "DrawTargetCapture.h" |
13 | | |
14 | | #include "BufferEdgePad.h" |
15 | | #include "BufferUnrotate.h" |
16 | | |
17 | | #ifdef BUILD_ARM_NEON |
18 | | #include "mozilla/arm.h" |
19 | | #include "LuminanceNEON.h" |
20 | | #endif |
21 | | |
22 | | namespace mozilla { |
23 | | namespace gfx { |
24 | | |
25 | | /** |
26 | | * Byte offsets of channels in a native packed gfxColor or cairo image surface. |
27 | | */ |
28 | | #ifdef IS_BIG_ENDIAN |
29 | | #define GFX_ARGB32_OFFSET_A 0 |
30 | | #define GFX_ARGB32_OFFSET_R 1 |
31 | | #define GFX_ARGB32_OFFSET_G 2 |
32 | | #define GFX_ARGB32_OFFSET_B 3 |
33 | | #else |
34 | 0 | #define GFX_ARGB32_OFFSET_A 3 |
35 | 0 | #define GFX_ARGB32_OFFSET_R 2 |
36 | 0 | #define GFX_ARGB32_OFFSET_G 1 |
37 | 0 | #define GFX_ARGB32_OFFSET_B 0 |
38 | | #endif |
39 | | |
40 | | // c = n / 255 |
41 | | // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5 |
42 | | static const uint8_t gsRGBToLinearRGBMap[256] = { |
43 | | 0, 0, 0, 0, 0, 0, 0, 1, |
44 | | 1, 1, 1, 1, 1, 1, 1, 1, |
45 | | 1, 1, 2, 2, 2, 2, 2, 2, |
46 | | 2, 2, 3, 3, 3, 3, 3, 3, |
47 | | 4, 4, 4, 4, 4, 5, 5, 5, |
48 | | 5, 6, 6, 6, 6, 7, 7, 7, |
49 | | 8, 8, 8, 8, 9, 9, 9, 10, |
50 | | 10, 10, 11, 11, 12, 12, 12, 13, |
51 | | 13, 13, 14, 14, 15, 15, 16, 16, |
52 | | 17, 17, 17, 18, 18, 19, 19, 20, |
53 | | 20, 21, 22, 22, 23, 23, 24, 24, |
54 | | 25, 25, 26, 27, 27, 28, 29, 29, |
55 | | 30, 30, 31, 32, 32, 33, 34, 35, |
56 | | 35, 36, 37, 37, 38, 39, 40, 41, |
57 | | 41, 42, 43, 44, 45, 45, 46, 47, |
58 | | 48, 49, 50, 51, 51, 52, 53, 54, |
59 | | 55, 56, 57, 58, 59, 60, 61, 62, |
60 | | 63, 64, 65, 66, 67, 68, 69, 70, |
61 | | 71, 72, 73, 74, 76, 77, 78, 79, |
62 | | 80, 81, 82, 84, 85, 86, 87, 88, |
63 | | 90, 91, 92, 93, 95, 96, 97, 99, |
64 | | 100, 101, 103, 104, 105, 107, 108, 109, |
65 | | 111, 112, 114, 115, 116, 118, 119, 121, |
66 | | 122, 124, 125, 127, 128, 130, 131, 133, |
67 | | 134, 136, 138, 139, 141, 142, 144, 146, |
68 | | 147, 149, 151, 152, 154, 156, 157, 159, |
69 | | 161, 163, 164, 166, 168, 170, 171, 173, |
70 | | 175, 177, 179, 181, 183, 184, 186, 188, |
71 | | 190, 192, 194, 196, 198, 200, 202, 204, |
72 | | 206, 208, 210, 212, 214, 216, 218, 220, |
73 | | 222, 224, 226, 229, 231, 233, 235, 237, |
74 | | 239, 242, 244, 246, 248, 250, 253, 255 |
75 | | }; |
76 | | |
77 | | static void |
78 | | ComputesRGBLuminanceMask(const uint8_t *aSourceData, |
79 | | int32_t aSourceStride, |
80 | | uint8_t *aDestData, |
81 | | int32_t aDestStride, |
82 | | const IntSize &aSize, |
83 | | float aOpacity) |
84 | 0 | { |
85 | | #ifdef BUILD_ARM_NEON |
86 | | if (mozilla::supports_neon()) { |
87 | | ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride, |
88 | | aDestData, aDestStride, |
89 | | aSize, aOpacity); |
90 | | return; |
91 | | } |
92 | | #endif |
93 | |
|
94 | 0 | int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity |
95 | 0 | int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity |
96 | 0 | int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721 |
97 | 0 | int32_t sourceOffset = aSourceStride - 4 * aSize.width; |
98 | 0 | const uint8_t *sourcePixel = aSourceData; |
99 | 0 | int32_t destOffset = aDestStride - aSize.width; |
100 | 0 | uint8_t *destPixel = aDestData; |
101 | 0 |
|
102 | 0 | for (int32_t y = 0; y < aSize.height; y++) { |
103 | 0 | for (int32_t x = 0; x < aSize.width; x++) { |
104 | 0 | uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A]; |
105 | 0 |
|
106 | 0 | if (a) { |
107 | 0 | *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] + |
108 | 0 | greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] + |
109 | 0 | blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8; |
110 | 0 | } else { |
111 | 0 | *destPixel = 0; |
112 | 0 | } |
113 | 0 | sourcePixel += 4; |
114 | 0 | destPixel++; |
115 | 0 | } |
116 | 0 | sourcePixel += sourceOffset; |
117 | 0 | destPixel += destOffset; |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | | static void |
122 | | ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData, |
123 | | int32_t aSourceStride, |
124 | | uint8_t *aDestData, |
125 | | int32_t aDestStride, |
126 | | const IntSize &aSize, |
127 | | float aOpacity) |
128 | 0 | { |
129 | 0 | int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity |
130 | 0 | int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity |
131 | 0 | int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721 |
132 | 0 | int32_t sourceOffset = aSourceStride - 4 * aSize.width; |
133 | 0 | const uint8_t *sourcePixel = aSourceData; |
134 | 0 | int32_t destOffset = aDestStride - aSize.width; |
135 | 0 | uint8_t *destPixel = aDestData; |
136 | 0 |
|
137 | 0 | for (int32_t y = 0; y < aSize.height; y++) { |
138 | 0 | for (int32_t x = 0; x < aSize.width; x++) { |
139 | 0 | uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A]; |
140 | 0 |
|
141 | 0 | // unpremultiply |
142 | 0 | if (a) { |
143 | 0 | if (a == 255) { |
144 | 0 | /* sRGB -> linearRGB -> intensity */ |
145 | 0 | *destPixel = |
146 | 0 | static_cast<uint8_t> |
147 | 0 | ((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] * |
148 | 0 | redFactor + |
149 | 0 | gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] * |
150 | 0 | greenFactor + |
151 | 0 | gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] * |
152 | 0 | blueFactor) >> 8); |
153 | 0 | } else { |
154 | 0 | uint8_t tempPixel[4]; |
155 | 0 | tempPixel[GFX_ARGB32_OFFSET_B] = |
156 | 0 | (255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a; |
157 | 0 | tempPixel[GFX_ARGB32_OFFSET_G] = |
158 | 0 | (255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a; |
159 | 0 | tempPixel[GFX_ARGB32_OFFSET_R] = |
160 | 0 | (255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a; |
161 | 0 |
|
162 | 0 | /* sRGB -> linearRGB -> intensity */ |
163 | 0 | *destPixel = |
164 | 0 | static_cast<uint8_t> |
165 | 0 | (((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] * |
166 | 0 | redFactor + |
167 | 0 | gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] * |
168 | 0 | greenFactor + |
169 | 0 | gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] * |
170 | 0 | blueFactor) >> 8) * (a / 255.0f)); |
171 | 0 | } |
172 | 0 | } else { |
173 | 0 | *destPixel = 0; |
174 | 0 | } |
175 | 0 | sourcePixel += 4; |
176 | 0 | destPixel++; |
177 | 0 | } |
178 | 0 | sourcePixel += sourceOffset; |
179 | 0 | destPixel += destOffset; |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | void |
184 | | DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT, |
185 | | const Matrix& aTransform) |
186 | 0 | { |
187 | 0 | if (aTransform.HasNonIntegerTranslation()) { |
188 | 0 | gfxWarning() << "Non integer translations are not supported for DrawCaptureDT at this time!"; |
189 | 0 | return; |
190 | 0 | } |
191 | 0 | static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform); |
192 | 0 | } |
193 | | |
194 | | void |
195 | | DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) |
196 | 0 | { |
197 | 0 | Matrix oldTransform = GetTransform(); |
198 | 0 | SetTransform(Matrix()); |
199 | 0 |
|
200 | 0 | RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); |
201 | 0 | for (uint32_t i = 0; i < aCount; i++) { |
202 | 0 | AppendRectToPath(pathBuilder, Rect(aRects[i])); |
203 | 0 | } |
204 | 0 | RefPtr<Path> path = pathBuilder->Finish(); |
205 | 0 | PushClip(path); |
206 | 0 |
|
207 | 0 | SetTransform(oldTransform); |
208 | 0 | } |
209 | | |
210 | | void |
211 | | DrawTarget::StrokeGlyphs(ScaledFont* aFont, |
212 | | const GlyphBuffer& aBuffer, |
213 | | const Pattern& aPattern, |
214 | | const StrokeOptions& aStrokeOptions, |
215 | | const DrawOptions& aOptions) |
216 | 0 | { |
217 | 0 | RefPtr<Path> path = aFont->GetPathForGlyphs(aBuffer, this); |
218 | 0 | Stroke(path, aPattern, aStrokeOptions, aOptions); |
219 | 0 | } |
220 | | |
221 | | already_AddRefed<SourceSurface> |
222 | | DrawTarget::IntoLuminanceSource(LuminanceType aMaskType, float aOpacity) |
223 | 0 | { |
224 | 0 | RefPtr<SourceSurface> surface = Snapshot(); |
225 | 0 | if (!surface) { |
226 | 0 | return nullptr; |
227 | 0 | } |
228 | 0 | |
229 | 0 | IntSize size = surface->GetSize(); |
230 | 0 |
|
231 | 0 | RefPtr<DataSourceSurface> maskSurface = surface->GetDataSurface(); |
232 | 0 | if (!maskSurface) { |
233 | 0 | return nullptr; |
234 | 0 | } |
235 | 0 | |
236 | 0 | DataSourceSurface::MappedSurface map; |
237 | 0 | if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) { |
238 | 0 | return nullptr; |
239 | 0 | } |
240 | 0 | |
241 | 0 | // Create alpha channel mask for output |
242 | 0 | RefPtr<DataSourceSurface> destMaskSurface = |
243 | 0 | Factory::CreateDataSourceSurface(size, SurfaceFormat::A8); |
244 | 0 | if (!destMaskSurface) { |
245 | 0 | return nullptr; |
246 | 0 | } |
247 | 0 | DataSourceSurface::MappedSurface destMap; |
248 | 0 | if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) { |
249 | 0 | return nullptr; |
250 | 0 | } |
251 | 0 | |
252 | 0 | switch (aMaskType) { |
253 | 0 | case LuminanceType::LUMINANCE: |
254 | 0 | { |
255 | 0 | ComputesRGBLuminanceMask(map.mData, map.mStride, |
256 | 0 | destMap.mData, destMap.mStride, |
257 | 0 | size, aOpacity); |
258 | 0 | break; |
259 | 0 | } |
260 | 0 | case LuminanceType::LINEARRGB: |
261 | 0 | { |
262 | 0 | ComputeLinearRGBLuminanceMask(map.mData, map.mStride, |
263 | 0 | destMap.mData, destMap.mStride, |
264 | 0 | size, aOpacity); |
265 | 0 | break; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | |
269 | 0 | maskSurface->Unmap(); |
270 | 0 | destMaskSurface->Unmap(); |
271 | 0 |
|
272 | 0 | return destMaskSurface.forget(); |
273 | 0 | } |
274 | | |
275 | | void |
276 | | DrawTarget::Blur(const AlphaBoxBlur& aBlur) |
277 | 0 | { |
278 | 0 | uint8_t* data; |
279 | 0 | IntSize size; |
280 | 0 | int32_t stride; |
281 | 0 | SurfaceFormat format; |
282 | 0 | if (!LockBits(&data, &size, &stride, &format)) { |
283 | 0 | gfxWarning() << "Cannot perform in-place blur on non-data DrawTarget"; |
284 | 0 | return; |
285 | 0 | } |
286 | 0 |
|
287 | 0 | // Sanity check that the blur size matches the draw target. |
288 | 0 | MOZ_ASSERT(size == aBlur.GetSize()); |
289 | 0 | MOZ_ASSERT(stride == aBlur.GetStride()); |
290 | 0 | aBlur.Blur(data); |
291 | 0 |
|
292 | 0 | ReleaseBits(data); |
293 | 0 | } |
294 | | |
295 | | void |
296 | | DrawTarget::PadEdges(const IntRegion& aRegion) |
297 | 0 | { |
298 | 0 | PadDrawTargetOutFromRegion(this, aRegion); |
299 | 0 | } |
300 | | |
301 | | bool |
302 | | DrawTarget::Unrotate(IntPoint aRotation) |
303 | 0 | { |
304 | 0 | unsigned char* data; |
305 | 0 | IntSize size; |
306 | 0 | int32_t stride; |
307 | 0 | SurfaceFormat format; |
308 | 0 |
|
309 | 0 | if (LockBits(&data, &size, &stride, &format)) { |
310 | 0 | uint8_t bytesPerPixel = BytesPerPixel(format); |
311 | 0 | BufferUnrotate(data, |
312 | 0 | size.width * bytesPerPixel, |
313 | 0 | size.height, stride, |
314 | 0 | aRotation.x * bytesPerPixel, |
315 | 0 | aRotation.y); |
316 | 0 | ReleaseBits(data); |
317 | 0 | return true; |
318 | 0 | } |
319 | 0 | return false; |
320 | 0 | } |
321 | | |
322 | | } // namespace gfx |
323 | | } // namespace mozilla |