Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  Blur.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "Blur.h"

#include <algorithm>
#include <math.h>
#include <string.h>

#include "NumericTools.h"

#include "2D.h"
#include "DataSurfaceHelpers.h"
#include "HelpersSkia.h"
#include "Tools.h"

#include "skia/include/core/SkCanvas.h"
#include "skia/include/core/SkSurface.h"
#include "skia/include/effects/SkImageFilters.h"

namespace mozilla {
namespace gfx {

template <typename T>
struct PixelValue {
  T value;

  explicit PixelValue(T aValue) : value(aValue) {}

  void Spread(const PixelValue& aOther) {
    value = std::max(value, aOther.value);
  }
};

template <>
struct PixelValue<uint32_t> {
  union {
    struct {
      uint8_t r;
      uint8_t g;
      uint8_t b;
      uint8_t a;
    };
    uint32_t value;
  };

  explicit PixelValue(uint32_t aValue) { value = aValue; }

  void Spread(const PixelValue& aOther) {
    r = std::max(r, aOther.r);
    g = std::max(g, aOther.g);
    b = std::max(b, aOther.b);
    a = std::max(a, aOther.a);
  }
};

template <typename T>
static void SpreadHorizontal(const T* aInput, T* aOutput, int32_t aRadius,
                             int32_t aWidth, int32_t aRows, int32_t aStride,
                             const IntRect& aSkipRect) {
  if (aRadius == 0) {
    memcpy(aOutput, aInput, aStride * aRows * sizeof(T));
    return;
  }

  bool skipRectCoversWholeRow =
      0 >= aSkipRect.X() && aWidth <= aSkipRect.XMost();
  for (int32_t y = 0; y < aRows; y++) {
    // Check whether the skip rect intersects this row. If the skip
    // rect covers the whole surface in this row, we can avoid
    // this row entirely (and any others along the skip rect).
    bool inSkipRectY = aSkipRect.ContainsY(y);
    if (inSkipRectY && skipRectCoversWholeRow) {
      y = aSkipRect.YMost() - 1;
      continue;
    }

    T* dst = &aOutput[aStride * y];
    for (int32_t x = 0; x < aWidth; x++) {
      // Check whether we are within the skip rect. If so, go
      // to the next point outside the skip rect.
      if (inSkipRectY && aSkipRect.ContainsX(x)) {
        x = aSkipRect.XMost();
        if (x >= aWidth) break;
      }

      int32_t sMin = std::max(x - aRadius, 0);
      int32_t sMax = std::min(x + aRadius, aWidth - 1);
      const auto* src =
          reinterpret_cast<const PixelValue<T>*>(&aInput[aStride * y + sMin]);
      PixelValue<T> v(0);
      for (int32_t s = sMin; s <= sMax; ++s) {
        v.Spread(*src);
        ++src;
      }
      *dst = v.value;
      ++dst;
    }
  }
}

template <typename T>
static void SpreadVertical(const T* aInput, T* aOutput, int32_t aRadius,
                           int32_t aWidth, int32_t aRows, int32_t aStride,
                           const IntRect& aSkipRect) {
  if (aRadius == 0) {
    memcpy(aOutput, aInput, aStride * aRows * sizeof(T));
    return;
  }

  bool skipRectCoversWholeColumn =
      0 >= aSkipRect.Y() && aRows <= aSkipRect.YMost();
  for (int32_t x = 0; x < aWidth; x++) {
    bool inSkipRectX = aSkipRect.ContainsX(x);
    if (inSkipRectX && skipRectCoversWholeColumn) {
      x = aSkipRect.XMost() - 1;
      continue;
    }

    T* dst = &aOutput[x];
    for (int32_t y = 0; y < aRows; y++) {
      // Check whether we are within the skip rect. If so, go
      // to the next point outside the skip rect.
      if (inSkipRectX && aSkipRect.ContainsY(y)) {
        y = aSkipRect.YMost();
        if (y >= aRows) break;
      }

      int32_t sMin = std::max(y - aRadius, 0);
      int32_t sMax = std::min(y + aRadius, aRows - 1);
      const auto* src =
          reinterpret_cast<const PixelValue<T>*>(&aInput[aStride * sMin + x]);
      PixelValue<T> v(0);
      for (int32_t s = sMin; s <= sMax; ++s) {
        v.Spread(*src);
        src += aStride;
      }
      *dst = v.value;
      dst += aStride;
    }
  }
}

GaussianBlur::GaussianBlur(const Rect& aRect, const IntSize& aSpreadRadius,
                           const Point& aSigma, const Rect* aDirtyRect,
                           const Rect* aSkipRect, SurfaceFormat aFormat,
                           bool aClamp) {
  Init(aRect, aSpreadRadius, aSigma, aDirtyRect, aSkipRect, aFormat, aClamp);
}

void GaussianBlur::Init(const Rect& aRect, const IntSize& aSpreadRadius,
                        const Point& aBlurSigma, const Rect* aDirtyRect,
                        const Rect* aSkipRect, SurfaceFormat aFormat,
                        bool aClamp) {
  switch (aFormat) {
    case SurfaceFormat::A8:
    case SurfaceFormat::B8G8R8A8:
    case SurfaceFormat::B8G8R8X8:
    case SurfaceFormat::R8G8B8A8:
    case SurfaceFormat::R8G8B8X8:
    case SurfaceFormat::A8R8G8B8:
    case SurfaceFormat::X8R8G8B8:
      break;
    default:
      MOZ_RELEASE_ASSERT(false"Unsupported format for GaussianBlur");
      break;
  }

  mFormat = aFormat;
  mClamp = aClamp;
  mSpreadRadius = aSpreadRadius;
  mBlurSigma = aBlurSigma;
  mBlurRadius = GaussianBlur::CalculateBlurRadius(aBlurSigma);

  Rect rect(aRect);
  rect.Inflate(Size(mBlurRadius + aSpreadRadius));
  rect.RoundOut();

  if (aDirtyRect) {
    // If we get passed a dirty rect from layout, we can minimize the
    // shadow size and make painting faster.
    mHasDirtyRect = true;
    mDirtyRect = *aDirtyRect;
    Rect requiredBlurArea = mDirtyRect.Intersect(rect);
    requiredBlurArea.Inflate(Size(mBlurRadius + aSpreadRadius));
    rect = requiredBlurArea.Intersect(rect);
  } else {
    mHasDirtyRect = false;
  }

  mRect = TruncatedToInt(rect);
  if (mRect.IsEmpty()) {
    return;
  }

  if (aSkipRect) {
    // If we get passed a skip rect, we can lower the amount of
    // blurring/spreading we need to do. We convert it to IntRect to avoid
    // expensive int<->float conversions if we were to use Rect instead.
    Rect skipRect = *aSkipRect;
    skipRect.Deflate(Size(mBlurRadius + aSpreadRadius));
    mSkipRect = RoundedIn(skipRect);
    mSkipRect = mSkipRect.Intersect(mRect);
    if (mSkipRect.IsEqualInterior(mRect)) {
      return;
    }

    mSkipRect -= mRect.TopLeft();
    if (mSkipRect.IsEmpty()) {
      mSkipRect = IntRect();
    }
  } else {
    mSkipRect = IntRect();
  }

  int32_t stride = StrideForFormatAndWidth(mFormat, mRect.Width());
  if (stride >= 0) {
    mStride = stride;

    // We need to leave room for an additional 3 bytes for a potential overrun
    // in our blurring code.
    size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.Height(), 3);
    if (size != 0) {
      mSurfaceAllocationSize = size;
    }
  }
}

GaussianBlur::GaussianBlur(const Point& aSigma, bool aClamp)
    : mBlurSigma(aSigma),
      mBlurRadius(CalculateBlurRadius(aSigma)),
      mClamp(aClamp) {}

IntSize GaussianBlur::GetSize() const { return mRect.Size(); }

SurfaceFormat GaussianBlur::GetFormat() const { return mFormat; }

int32_t GaussianBlur::GetStride() const { return mStride; }

IntRect GaussianBlur::GetRect() const { return mRect; }

Rect* GaussianBlur::GetDirtyRect() {
  if (mHasDirtyRect) {
    return &mDirtyRect;
  }

  return nullptr;
}

size_t GaussianBlur::GetSurfaceAllocationSize() const {
  return mSurfaceAllocationSize;
}

bool GaussianBlur::Spread(uint8_t* aData, int32_t aStride, const IntSize& aSize,
                          SurfaceFormat aFormat) const {
  size_t bufSize = BufferSizeFromStrideAndHeight(aStride, aSize.height);
  if (!bufSize) {
    return false;
  }
  uint8_t* tmpData = (uint8_t*)calloc(1, bufSize);
  if (!tmpData) {
    return false;
  }
  if (aFormat == SurfaceFormat::A8) {
    SpreadHorizontal(aData, tmpData, mSpreadRadius.width, aSize.width,
                     aSize.height, aStride, mSkipRect);
    SpreadVertical(tmpData, aData, mSpreadRadius.height, aSize.width,
                   aSize.height, aStride, mSkipRect);
  } else {
    uint32_t* data32 = reinterpret_cast<uint32_t*>(aData);
    uint32_t* tmpData32 = reinterpret_cast<uint32_t*>(tmpData);
    int32_t stride32 = aStride / sizeof(uint32_t);
    SpreadHorizontal(data32, tmpData32, mSpreadRadius.width, aSize.width,
                     aSize.height, stride32, mSkipRect);
    SpreadVertical(tmpData32, data32, mSpreadRadius.height, aSize.width,
                   aSize.height, stride32, mSkipRect);
  }
  free(tmpData);
  return true;
}

void GaussianBlur::Blur(uint8_t* aData, int32_t aStride, const IntSize& aSize,
                        SurfaceFormat aFormat) const {
  if (!aData || aStride <= 0) {
    return;
  }
  if (aFormat == SurfaceFormat::UNKNOWN) {
    aFormat = mFormat;
    if (aFormat == SurfaceFormat::UNKNOWN) {
      return;
    }
  }
  if (mBlurRadius.width > 0 || mBlurRadius.height > 0 ||
      mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
    if (sk_sp<SkSurface> surface = SkSurfaces::WrapPixels(
            MakeSkiaImageInfo(aSize, aFormat), aData, aStride)) {
      BlurSkSurface(surface.get());
    }
  }
}

bool GaussianBlur::BlurSkSurface(SkSurface* aSurface) const {
  IntSize size(aSurface->width(), aSurface->height());
  MOZ_ASSERT(mRect.IsEmpty() || size == mRect.Size());
  SkCanvas* canvas = aSurface->getCanvas();
  if (!canvas) {
    return false;
  }

  if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
    SkImageInfo info;
    size_t rowBytes = 0;
    uint8_t* pixels = (uint8_t*)canvas->accessTopLayerPixels(&info, &rowBytes);
    if (!pixels ||
        !Spread(pixels, rowBytes, IntSize(info.width(), info.height()),
                SkiaColorTypeToGfxFormat(info.colorType()))) {
      return false;
    }
  }

  if (mBlurRadius.width <= 0 && mBlurRadius.height <= 0) {
    return true;
  }

  sk_sp<SkImage> snapshot = aSurface->makeImageSnapshot();
  if (!snapshot) {
    return false;
  }
  canvas->save();
  canvas->resetMatrix();
  SkPaint blurPaint;
  blurPaint.setBlendMode(SkBlendMode::kSrc);
  sk_sp<SkImageFilter> blurFilter(SkImageFilters::Blur(
      mBlurSigma.x, mBlurSigma.y,
      mClamp ? SkTileMode::kClamp : SkTileMode::kDecal, nullptr));
  blurPaint.setImageFilter(blurFilter);
  SkSamplingOptions sampling(SkFilterMode::kNearest);
  auto constraint = SkCanvas::kFast_SrcRectConstraint;
  if (mSkipRect.IsEmpty()) {
    canvas->drawImage(snapshot, 00, sampling, &blurPaint);
  } else {
    SkRect top = SkRect::MakeIWH(size.width, size.height);
    if (top.intersect(SkRect::MakeLTRB(00, size.width, mSkipRect.y))) {
      canvas->drawImageRect(snapshot, top, top, sampling, &blurPaint,
                            constraint);
    }
    SkRect left = SkRect::MakeIWH(size.width, size.height);
    if (left.intersect(
            SkRect::MakeLTRB(0, mSkipRect.y, mSkipRect.x, mSkipRect.YMost()))) {
      canvas->drawImageRect(snapshot, left, left, sampling, &blurPaint,
                            constraint);
    }
    SkRect right = SkRect::MakeIWH(size.width, size.height);
    if (right.intersect(SkRect::MakeLTRB(mSkipRect.XMost(), mSkipRect.y,
                                         size.width, mSkipRect.YMost()))) {
      canvas->drawImageRect(snapshot, right, right, sampling, &blurPaint,
                            constraint);
    }
    SkRect bottom = SkRect::MakeIWH(size.width, size.height);
    if (bottom.intersect(
            SkRect::MakeLTRB(0, mSkipRect.YMost(), size.width, size.height))) {
      canvas->drawImageRect(snapshot, bottom, bottom, sampling, &blurPaint,
                            constraint);
    }
  }
  canvas->restore();
  return true;
}

/**
 * Compute the box blur size (which we're calling the blur radius) from
 * the standard deviation.
 *
 * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
 * approximating a Gaussian using box blurs.  This yields quite a good
 * approximation for a Gaussian.  Then we multiply this by 1.5 since our
 * code wants the radius of the entire triple-box-blur kernel instead of
 * the diameter of an individual box blur.  For more details, see:
 *   http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
 *   https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
 */

constexpr double sqrt_2_PI = 0x1.40d931ff62705p+1;  // sqrt is not constexpr
static constexpr Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt_2_PI / 4) * 1.5);

IntSize GaussianBlur::CalculateBlurRadius(const Point& aStd) {
  IntSize size(
      static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
      static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));

  return size;
}

Float GaussianBlur::CalculateBlurSigma(int32_t aBlurRadius) {
  return aBlurRadius / GAUSSIAN_SCALE_FACTOR;
}

}  // namespace gfx
}  // namespace mozilla

Messung V0.5 in Prozent
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142