tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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