DrawTarget.cpp (13849B)
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 "Blur.h" 9 #include "Logging.h" 10 #include "PathHelpers.h" 11 #include "SourceSurfaceRawData.h" 12 #include "Tools.h" 13 14 #include "BufferEdgePad.h" 15 #include "BufferUnrotate.h" 16 17 #include "FilterSupport.h" 18 19 #ifdef USE_NEON 20 # include "mozilla/arm.h" 21 # include "LuminanceNEON.h" 22 #endif 23 24 namespace mozilla { 25 namespace gfx { 26 27 /** 28 * Byte offsets of channels in a native packed gfxColor or cairo image surface. 29 */ 30 #ifdef IS_BIG_ENDIAN 31 # define GFX_ARGB32_OFFSET_A 0 32 # define GFX_ARGB32_OFFSET_R 1 33 # define GFX_ARGB32_OFFSET_G 2 34 # define GFX_ARGB32_OFFSET_B 3 35 #else 36 # define GFX_ARGB32_OFFSET_A 3 37 # define GFX_ARGB32_OFFSET_R 2 38 # define GFX_ARGB32_OFFSET_G 1 39 # define GFX_ARGB32_OFFSET_B 0 40 #endif 41 42 // c = n / 255 43 // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5 44 static const uint8_t gsRGBToLinearRGBMap[256] = { 45 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 46 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 47 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 48 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 49 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 50 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 51 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 52 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 53 48, 49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 54 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 55 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 95, 56 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 114, 115, 57 116, 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133, 134, 136, 138, 58 139, 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159, 161, 163, 59 164, 166, 168, 170, 171, 173, 175, 177, 179, 181, 183, 184, 186, 188, 190, 60 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 61 222, 224, 226, 229, 231, 233, 235, 237, 239, 242, 244, 246, 248, 250, 253, 62 255}; 63 64 static void ComputesRGBLuminanceMask(const uint8_t* aSourceData, 65 int32_t aSourceStride, uint8_t* aDestData, 66 int32_t aDestStride, const IntSize& aSize, 67 float aOpacity) { 68 #ifdef USE_NEON 69 if (mozilla::supports_neon()) { 70 ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride, aDestData, 71 aDestStride, aSize, aOpacity); 72 return; 73 } 74 #endif 75 76 int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity 77 int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity 78 int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721 79 int32_t sourceOffset = aSourceStride - 4 * aSize.width; 80 const uint8_t* sourcePixel = aSourceData; 81 int32_t destOffset = aDestStride - aSize.width; 82 uint8_t* destPixel = aDestData; 83 84 for (int32_t y = 0; y < aSize.height; y++) { 85 for (int32_t x = 0; x < aSize.width; x++) { 86 uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A]; 87 88 if (a) { 89 *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] + 90 greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] + 91 blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 92 8; 93 } else { 94 *destPixel = 0; 95 } 96 sourcePixel += 4; 97 destPixel++; 98 } 99 sourcePixel += sourceOffset; 100 destPixel += destOffset; 101 } 102 } 103 104 static void ComputeLinearRGBLuminanceMask( 105 const uint8_t* aSourceData, int32_t aSourceStride, uint8_t* aDestData, 106 int32_t aDestStride, const IntSize& aSize, float aOpacity) { 107 int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity 108 int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity 109 int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721 110 int32_t sourceOffset = aSourceStride - 4 * aSize.width; 111 const uint8_t* sourcePixel = aSourceData; 112 int32_t destOffset = aDestStride - aSize.width; 113 uint8_t* destPixel = aDestData; 114 115 for (int32_t y = 0; y < aSize.height; y++) { 116 for (int32_t x = 0; x < aSize.width; x++) { 117 uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A]; 118 119 // unpremultiply 120 if (a) { 121 if (a == 255) { 122 /* sRGB -> linearRGB -> intensity */ 123 *destPixel = static_cast<uint8_t>( 124 (gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] * 125 redFactor + 126 gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] * 127 greenFactor + 128 gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] * 129 blueFactor) >> 130 8); 131 } else { 132 uint8_t tempPixel[4]; 133 tempPixel[GFX_ARGB32_OFFSET_B] = 134 (255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a; 135 tempPixel[GFX_ARGB32_OFFSET_G] = 136 (255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a; 137 tempPixel[GFX_ARGB32_OFFSET_R] = 138 (255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a; 139 140 /* sRGB -> linearRGB -> intensity */ 141 *destPixel = static_cast<uint8_t>( 142 ((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] * 143 redFactor + 144 gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] * 145 greenFactor + 146 gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] * 147 blueFactor) >> 148 8) * 149 (a / 255.0f)); 150 } 151 } else { 152 *destPixel = 0; 153 } 154 sourcePixel += 4; 155 destPixel++; 156 } 157 sourcePixel += sourceOffset; 158 destPixel += destOffset; 159 } 160 } 161 162 void DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, 163 uint32_t aCount) { 164 Matrix oldTransform = GetTransform(); 165 SetTransform(Matrix()); 166 167 RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); 168 for (uint32_t i = 0; i < aCount; i++) { 169 AppendRectToPath(pathBuilder, Rect(aRects[i])); 170 } 171 RefPtr<Path> path = pathBuilder->Finish(); 172 PushClip(path); 173 174 SetTransform(oldTransform); 175 } 176 177 void DrawTarget::FillRoundedRect(const RoundedRect& aRect, 178 const Pattern& aPattern, 179 const DrawOptions& aOptions) { 180 RefPtr<Path> path = MakePathForRoundedRect(*this, aRect.rect, aRect.corners); 181 Fill(path, aPattern, aOptions); 182 } 183 184 void DrawTarget::StrokeCircle(const Point& aOrigin, float radius, 185 const Pattern& aPattern, 186 const StrokeOptions& aStrokeOptions, 187 const DrawOptions& aOptions) { 188 RefPtr<Path> path = MakePathForCircle(*this, aOrigin, radius); 189 Stroke(path, aPattern, aStrokeOptions, aOptions); 190 } 191 192 void DrawTarget::FillCircle(const Point& aOrigin, float radius, 193 const Pattern& aPattern, 194 const DrawOptions& aOptions) { 195 RefPtr<Path> path = MakePathForCircle(*this, aOrigin, radius); 196 Fill(path, aPattern, aOptions); 197 } 198 199 void DrawTarget::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, 200 const Pattern& aPattern, 201 const StrokeOptions& aStrokeOptions, 202 const DrawOptions& aOptions) { 203 if (RefPtr<Path> path = aFont->GetPathForGlyphs(aBuffer, this)) { 204 Stroke(path, aPattern, aStrokeOptions, aOptions); 205 } 206 } 207 208 already_AddRefed<SourceSurface> DrawTarget::IntoLuminanceSource( 209 LuminanceType aMaskType, float aOpacity) { 210 // The default IntoLuminanceSource implementation needs a format of B8G8R8A8. 211 if (mFormat != SurfaceFormat::B8G8R8A8) { 212 return nullptr; 213 } 214 215 RefPtr<SourceSurface> surface = Snapshot(); 216 if (!surface) { 217 return nullptr; 218 } 219 220 IntSize size = surface->GetSize(); 221 222 RefPtr<DataSourceSurface> maskSurface = surface->GetDataSurface(); 223 if (!maskSurface) { 224 return nullptr; 225 } 226 227 DataSourceSurface::MappedSurface map; 228 if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) { 229 return nullptr; 230 } 231 232 // Create alpha channel mask for output 233 RefPtr<SourceSurfaceAlignedRawData> destMaskSurface = 234 new SourceSurfaceAlignedRawData; 235 if (!destMaskSurface->Init(size, SurfaceFormat::A8, false, 0)) { 236 return nullptr; 237 } 238 DataSourceSurface::MappedSurface destMap; 239 if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) { 240 return nullptr; 241 } 242 243 switch (aMaskType) { 244 case LuminanceType::LUMINANCE: { 245 ComputesRGBLuminanceMask(map.mData, map.mStride, destMap.mData, 246 destMap.mStride, size, aOpacity); 247 break; 248 } 249 case LuminanceType::LINEARRGB: { 250 ComputeLinearRGBLuminanceMask(map.mData, map.mStride, destMap.mData, 251 destMap.mStride, size, aOpacity); 252 break; 253 } 254 } 255 256 maskSurface->Unmap(); 257 destMaskSurface->Unmap(); 258 259 return destMaskSurface.forget(); 260 } 261 262 void DrawTarget::Blur(const GaussianBlur& aBlur) { 263 uint8_t* data; 264 IntSize size; 265 int32_t stride; 266 SurfaceFormat format; 267 if (!LockBits(&data, &size, &stride, &format)) { 268 gfxWarning() << "Cannot perform in-place blur on non-data DrawTarget"; 269 return; 270 } 271 272 aBlur.Blur(data, stride, size, format); 273 274 ReleaseBits(data); 275 } 276 277 void DrawTarget::PadEdges(const IntRegion& aRegion) { 278 PadDrawTargetOutFromRegion(this, aRegion); 279 } 280 281 bool DrawTarget::Unrotate(IntPoint aRotation) { 282 unsigned char* data; 283 IntSize size; 284 int32_t stride; 285 SurfaceFormat format; 286 287 if (LockBits(&data, &size, &stride, &format)) { 288 uint8_t bytesPerPixel = BytesPerPixel(format); 289 BufferUnrotate(data, size.width * bytesPerPixel, size.height, stride, 290 aRotation.x * bytesPerPixel, aRotation.y); 291 ReleaseBits(data); 292 return true; 293 } 294 return false; 295 } 296 297 int32_t ShadowOptions::BlurRadius() const { 298 return GaussianBlur::CalculateBlurRadius(Point(mSigma, mSigma)).width; 299 } 300 301 void DrawTarget::DrawShadow(const Path* aPath, const Pattern& aPattern, 302 const ShadowOptions& aShadow, 303 const DrawOptions& aOptions, 304 const StrokeOptions* aStrokeOptions) { 305 // Get the approximate bounds of the source path 306 Rect bounds = aPath->GetFastBounds(GetTransform(), aStrokeOptions); 307 if (bounds.IsEmpty()) { 308 return; 309 } 310 // Inflate the bounds by the blur radius 311 bounds += aShadow.mOffset; 312 int32_t blurRadius = aShadow.BlurRadius(); 313 bounds.Inflate(blurRadius); 314 bounds.RoundOut(); 315 // Check if the bounds intersect the viewport 316 Rect viewport(GetRect()); 317 viewport.Inflate(blurRadius); 318 bounds = bounds.Intersect(viewport); 319 IntRect intBounds; 320 if (bounds.IsEmpty() || !bounds.ToIntRect(&intBounds) || 321 !CanCreateSimilarDrawTarget(intBounds.Size(), SurfaceFormat::A8)) { 322 return; 323 } 324 // Create a draw target for drawing the shadow mask with enough room for blur 325 RefPtr<DrawTarget> shadowTarget = CreateShadowDrawTarget( 326 intBounds.Size(), SurfaceFormat::A8, aShadow.mSigma); 327 if (shadowTarget) { 328 // See bug 1524554. 329 shadowTarget->ClearRect(Rect()); 330 } 331 if (!shadowTarget || !shadowTarget->IsValid()) { 332 return; 333 } 334 // Draw the path into the target for the initial shadow mask 335 Point offset = Point(intBounds.TopLeft()) - aShadow.mOffset; 336 shadowTarget->SetTransform(GetTransform().PostTranslate(-offset)); 337 DrawOptions shadowDrawOptions( 338 aOptions.mAlpha, CompositionOp::OP_OVER, 339 blurRadius > 1 ? AntialiasMode::NONE : aOptions.mAntialiasMode); 340 if (aStrokeOptions) { 341 shadowTarget->Stroke(aPath, aPattern, *aStrokeOptions, shadowDrawOptions); 342 } else { 343 shadowTarget->Fill(aPath, aPattern, shadowDrawOptions); 344 } 345 RefPtr<SourceSurface> snapshot = shadowTarget->Snapshot(); 346 // Finally, hand a snapshot of the mask to DrawSurfaceWithShadow for the 347 // final shadow blur 348 if (snapshot) { 349 DrawSurfaceWithShadow(snapshot, offset, aShadow, aOptions.mCompositionOp); 350 } 351 } 352 353 already_AddRefed<SourceSurface> DrawTarget::ResolveFilterInput( 354 const Path* aPath, const Pattern& aPattern, const IntRect& aSourceRect, 355 const Matrix& aDestTransform, const DrawOptions& aOptions, 356 const StrokeOptions* aStrokeOptions, SurfaceFormat aFormat) { 357 if (!CanCreateSimilarDrawTarget(aSourceRect.Size(), aFormat)) { 358 return nullptr; 359 } 360 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(aSourceRect.Size(), aFormat); 361 if (dt) { 362 // See bug 1524554. 363 dt->ClearRect(Rect()); 364 } 365 if (!dt || !dt->IsValid()) { 366 return nullptr; 367 } 368 dt->SetTransform( 369 Matrix(aDestTransform).PostTranslate(-aSourceRect.TopLeft())); 370 if (aStrokeOptions) { 371 dt->Stroke(aPath, aPattern, *aStrokeOptions, aOptions); 372 } else { 373 dt->Fill(aPath, aPattern, aOptions); 374 } 375 return dt->Snapshot(); 376 } 377 378 already_AddRefed<FilterNode> DrawTarget::DeferFilterInput( 379 const Path* aPath, const Pattern& aPattern, const IntRect& aSourceRect, 380 const IntPoint& aDestOffset, const DrawOptions& aOptions, 381 const StrokeOptions* aStrokeOptions) { 382 RefPtr<SourceSurface> surface = ResolveFilterInput( 383 aPath, aPattern, aSourceRect, GetTransform().PostTranslate(aDestOffset), 384 aOptions, aStrokeOptions); 385 if (!surface) { 386 return nullptr; 387 } 388 return FilterWrappers::ForSurface(this, surface, aSourceRect.TopLeft()); 389 } 390 391 } // namespace gfx 392 } // namespace mozilla