Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/windows/tests/gtest/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 14 kB image not shown  

Quelle  TestWindowGfx.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <stdint.h>

#include "Image.h"
#include "ImageFactory.h"
#include "imgITools.h"
#include "mozilla/Base64.h"
#include "mozilla/Encoding.h"
#include "mozilla/gtest/MozAssertions.h"
#include "mozilla/Preferences.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/SystemPrincipal.h"
#include "mozilla/UniquePtr.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsILoadInfo.h"
#include "nsISVGPaintContext.h"
#include "nsMimeTypes.h"
#include "nsStreamUtils.h"
#include "nsWindowGfx.h"
#include "SystemPrincipal.h"

#include "gtest/gtest.h"

using namespace mozilla;
using namespace mozilla::image;

const char* SVG_GREEN_CIRCLE =
    "1.0\" encoding=\"UTF-8\"?> \
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\" viewBox=\"0 0 100 100\" version=\"1.1\"> \
  <circle fill=\"#00FF00\" stroke=\"#FF0000\" stroke-width=\"20\" cx=\"50\" cy=\"50\" r=\"40\" /> \
</svg> \
";

const char* SVG_UNSIZED_CIRCLE =
    "1.0\" encoding=\"UTF-8\"?> \
<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" version=\"1.1\"> \
  <circle fill=\"#00FF00\" stroke=\"#FF0000\" stroke-width=\"20\" cx=\"50\" cy=\"50\" r=\"40\" /> \
</svg> \
";

const char* SVG_CONTEXT_CIRCLE =
    "1.0\" encoding=\"UTF-8\"?> \
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\" viewBox=\"0 0 100 100\" version=\"1.1\"> \
  <circle fill=\"context-fill\" stroke=\"context-stroke\" stroke-width=\"20\" cx=\"50\" cy=\"50\" r=\"40\" /> \
</svg> \
";

// Circle's radius is 40 but the total radius includes half the stroke width.
#define CIRCLE_TOTAL_AREA (M_PI * 50 * 50)
// The fill area's radius is the circle's radius minus half the stroke width.
#define CIRCLE_FILL_AREA (M_PI * 30 * 30)
#define CIRCLE_STROKE_AREA (CIRCLE_TOTAL_AREA - CIRCLE_FILL_AREA)

// Allow 2% margin of error to allow for blending
#define ASSERT_NEARLY(val1, val2)   \
  {                                 \
    ASSERT_GT(val1, (val2) * 0.98); \
    ASSERT_LT(val1, (val2) * 1.02); \
  }

class SvgPaintContext : public nsISVGPaintContext {
 public:
  NS_DECL_ISUPPORTS

  nsCString mStrokeColor;
  nsCString mFillColor;

  SvgPaintContext(const char* aStroke, const char* aFill)
      : mStrokeColor(aStroke), mFillColor(aFill) {}

  NS_IMETHODIMP GetStrokeColor(nsACString& color) override {
    color = mStrokeColor;
    return NS_OK;
  }

  NS_IMETHODIMP GetStrokeOpacity(float* opacity) override {
    *opacity = 1.0;
    return NS_OK;
  }

  NS_IMETHODIMP GetFillColor(nsACString& color) override {
    color = mFillColor;
    return NS_OK;
  }

  NS_IMETHODIMP GetFillOpacity(float* opacity) override {
    *opacity = 1.0;
    return NS_OK;
  }

 private:
  virtual ~SvgPaintContext() {};
};

NS_IMPL_ISUPPORTS(SvgPaintContext, nsISVGPaintContext);

class ImageLoadListener : public IProgressObserver {
 public:
  NS_INLINE_DECL_REFCOUNTING(ImageLoadListener, override)

  virtual void OnLoadComplete(bool aLastPart) override { mIsLoaded = true; }

  // Other notifications are ignored.
  virtual void Notify(int32_t aType,
                      const nsIntRect* aRect = nullptr) override {}
  virtual void SetHasImage() override {}
  virtual bool NotificationsDeferred() const override { return false; }
  virtual void MarkPendingNotify() override {}
  virtual void ClearPendingNotify() override {}

  boolean mIsLoaded{};

 private:
  virtual ~ImageLoadListener() {};
};

void LoadImage(const char* aData, imgIContainer** aImage) {
  nsCString svgUri;
  nsresult rv = Base64Encode(aData, strlen(aData), svgUri);
  ASSERT_NS_SUCCEEDED(rv);
  svgUri.Insert("data:" IMAGE_SVG_XML ";base64,", 0);

  nsCOMPtr<nsIURI> uri;
  rv = NS_NewURI(getter_AddRefs(uri), svgUri, UTF_8_ENCODING, nullptr);
  ASSERT_NS_SUCCEEDED(rv);

  nsCOMPtr<nsIPrincipal> principal = SystemPrincipal::Get();
  nsCOMPtr<nsIChannel> channel;
  rv = NS_NewChannel(getter_AddRefs(channel), uri, principal,
                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
                     nsContentPolicyType::TYPE_IMAGE);

  RefPtr<ImageLoadListener> listener = new ImageLoadListener();
  RefPtr<ProgressTracker> tracker = new ProgressTracker();
  tracker->AddObserver(listener);
  RefPtr<Image> image = ImageFactory::CreateImage(
      channel, tracker, nsCString(IMAGE_SVG_XML), uri, false, 0);
  ASSERT_FALSE(image->HasError());

  nsCOMPtr<nsIInputStream> stream;
  rv = channel->Open(getter_AddRefs(stream));
  ASSERT_NS_SUCCEEDED(rv);

  uint64_t size;
  rv = stream->Available(&size);
  ASSERT_NS_SUCCEEDED(rv);
  ASSERT_EQ(size, strlen(aData));

  rv = image->OnImageDataAvailable(channel, stream, 0, size);
  ASSERT_NS_SUCCEEDED(rv);

  // Let the Image know we've sent all the data.
  rv = image->OnImageDataComplete(channel, NS_OK, true);
  ASSERT_NS_SUCCEEDED(rv);

  // The final load event from the SVG document is dispatched asynchronously so
  // wait for that to happen.
  MOZ_ALWAYS_TRUE(
      SpinEventLoopUntil("windows:widget:TEST(TestWindowGfx, CreateIcon)"_ns,
                         [&listener]() { return listener->mIsLoaded; }));

  image.forget(aImage);
}

void ConvertToRaster(imgIContainer* vectorImage, imgIContainer** aImage) {
  // First we encode it as a png image.
  nsCOMPtr<imgITools> imgTools =
      do_CreateInstance("@mozilla.org/image/tools;1");

  nsCOMPtr<nsIInputStream> stream;
  nsresult rv = imgTools->EncodeImage(vectorImage, "image/png"_ns, u""_ns,
                                      getter_AddRefs(stream));
  ASSERT_NS_SUCCEEDED(rv);

  uint64_t size;
  rv = stream->Available(&size);

  // And then we load the image again as a raster imgIContainer
  RefPtr<image::Image> image =
      ImageFactory::CreateAnonymousImage("image/png"_ns, size);
  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
  ASSERT_FALSE(image->HasError());

  rv = image->OnImageDataAvailable(nullptr, stream, 0, size);
  ASSERT_NS_SUCCEEDED(rv);

  // Let the Image know we've sent all the data.
  rv = image->OnImageDataComplete(nullptr, NS_OK, true);
  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
  ASSERT_NS_SUCCEEDED(rv);

  image.forget(aImage);
}

void CountPixels(ICONINFO& ii, BITMAP& bm, double* redCount, double* greenCount,
                 double* blueCount) {
  BITMAPINFOHEADER bi;
  bi.biSize = sizeof(BITMAPINFOHEADER);
  bi.biWidth = bm.bmWidth;
  bi.biHeight = bm.bmHeight;
  bi.biPlanes = 1;
  bi.biBitCount = 32;
  bi.biCompression = BI_RGB;
  bi.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrUsed = 0;
  bi.biClrImportant = 0;

  auto stride = GDI_WIDTHBYTES(bm.bmWidth * 32);
  auto dataLength = stride * bm.bmHeight;

  UniquePtr<uint8_t[]> bitmapData(new uint8_t[dataLength]);

  *redCount = 0;
  *greenCount = 0;
  *blueCount = 0;

  int lines =
      GetDIBits(::GetDC(nullptr), ii.hbmColor, 0, (UINT)bm.bmHeight,
                (void*)bitmapData.get(), (BITMAPINFO*)&bi, DIB_RGB_COLORS);
  if (lines != bm.bmHeight) {
    return;
  }

  for (long y = 0; y < bm.bmHeight; y++) {
    auto index = stride * y;

    for (long x = 0; x < bm.bmWidth; x++) {
      // Pixels are in BGRA format.
      double blue = bitmapData[index++] / 255.0;
      double green = bitmapData[index++] / 255.0;
      double red = bitmapData[index++] / 255.0;
      double alpha = bitmapData[index++] / 255.0;

      *redCount += red * alpha;
      *greenCount += green * alpha;
      *blueCount += blue * alpha;
    }
  }
}

// Tests that we can scale down an image
TEST(TestWindowGfx, CreateIcon_ScaledDown)
{
  auto Test = [](imgIContainer* image) {
    HICON icon;
    nsresult rv =
        nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
                                LayoutDeviceIntSize(50, 50), &icon);
    ASSERT_NS_SUCCEEDED(rv);

    ICONINFO ii;
    BOOL fResult = ::GetIconInfo(icon, &ii);
    ASSERT_TRUE(fResult);

    BITMAP bm;
    fResult = ::GetObject(ii.hbmColor, sizeof(bm), &bm) == sizeof(bm);
    ASSERT_TRUE(fResult);

    ASSERT_EQ(bm.bmWidth, 50);
    ASSERT_EQ(bm.bmHeight, 50);

    double redCount, greenCount, blueCount;
    CountPixels(ii, bm, &redCount, &greenCount, &blueCount);

    // We've scaled the image down to a quarter of its size.
    double fillArea = CIRCLE_FILL_AREA / 4;
    double strokeArea = CIRCLE_STROKE_AREA / 4;

    ASSERT_NEARLY(redCount, strokeArea);
    ASSERT_NEARLY(greenCount, fillArea);
    ASSERT_EQ(blueCount, 0.0);

    if (ii.hbmMask) DeleteObject(ii.hbmMask);
    if (ii.hbmColor) DeleteObject(ii.hbmColor);

    ::DestroyIcon(icon);
  };

  nsCOMPtr<imgIContainer> vectorImage;
  LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(vectorImage));

  Test(vectorImage);

  nsCOMPtr<imgIContainer> rasterImage;
  ConvertToRaster(vectorImage, getter_AddRefs(rasterImage));

  Test(rasterImage);
}

// Tests that we can scale up an image
TEST(TestWindowGfx, CreateIcon_ScaledUp)
{
  auto Test = [](imgIContainer* image) {
    HICON icon;
    nsresult rv =
        nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
                                LayoutDeviceIntSize(200, 200), &icon);
    ASSERT_NS_SUCCEEDED(rv);

    ICONINFO ii;
    BOOL fResult = ::GetIconInfo(icon, &ii);
    ASSERT_TRUE(fResult);

    BITMAP bm;
    fResult = ::GetObject(ii.hbmColor, sizeof(bm), &bm) == sizeof(bm);
    ASSERT_TRUE(fResult);

    ASSERT_EQ(bm.bmWidth, 200);
    ASSERT_EQ(bm.bmHeight, 200);

    double redCount, greenCount, blueCount;
    CountPixels(ii, bm, &redCount, &greenCount, &blueCount);

    // We've scaled the image up to four times its size.
    double fillArea = CIRCLE_FILL_AREA * 4;
    double strokeArea = CIRCLE_STROKE_AREA * 4;

    ASSERT_NEARLY(redCount, strokeArea);
    ASSERT_NEARLY(greenCount, fillArea);
    ASSERT_EQ(blueCount, 0.0);

    if (ii.hbmMask) DeleteObject(ii.hbmMask);
    if (ii.hbmColor) DeleteObject(ii.hbmColor);

    ::DestroyIcon(icon);
  };

  nsCOMPtr<imgIContainer> vectorImage;
  LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(vectorImage));

  Test(vectorImage);

  nsCOMPtr<imgIContainer> rasterImage;
  ConvertToRaster(vectorImage, getter_AddRefs(rasterImage));

  Test(rasterImage);
}

// Tests that we can render an image at its intrinsic size
TEST(TestWindowGfx, CreateIcon_Intrinsic)
{
  auto Test = [](imgIContainer* image) {
    HICON icon;
    nsresult rv =
        nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
                                LayoutDeviceIntSize(), &icon);
    ASSERT_NS_SUCCEEDED(rv);

    ICONINFO ii;
    BOOL fResult = ::GetIconInfo(icon, &ii);
    ASSERT_TRUE(fResult);

    BITMAP bm;
    fResult = ::GetObject(ii.hbmColor, sizeof(bm), &bm) == sizeof(bm);
    ASSERT_TRUE(fResult);

    ASSERT_EQ(bm.bmWidth, 100);
    ASSERT_EQ(bm.bmHeight, 100);

    double redCount, greenCount, blueCount;
    CountPixels(ii, bm, &redCount, &greenCount, &blueCount);

    ASSERT_NEARLY(redCount, CIRCLE_STROKE_AREA);
    ASSERT_NEARLY(greenCount, CIRCLE_FILL_AREA);
    ASSERT_EQ(blueCount, 0.0);

    if (ii.hbmMask) DeleteObject(ii.hbmMask);
    if (ii.hbmColor) DeleteObject(ii.hbmColor);

    ::DestroyIcon(icon);
  };

  nsCOMPtr<imgIContainer> vectorImage;
  LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(vectorImage));

  Test(vectorImage);

  nsCOMPtr<imgIContainer> rasterImage;
  ConvertToRaster(vectorImage, getter_AddRefs(rasterImage));

  Test(rasterImage);
}

// If an SVG has no intrinsic size and we don't provide one we fail.
TEST(TestWindowGfx, CreateIcon_SVG_NoSize)
{
  nsCOMPtr<imgIContainer> image;
  LoadImage(SVG_UNSIZED_CIRCLE, getter_AddRefs(image));

  HICON icon;
  nsresult rv =
      nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
                              LayoutDeviceIntSize(), &icon);
  ASSERT_EQ(rv, NS_ERROR_FAILURE);
}

// But we can still render an SVG with no intrinsic size as long as we provide
// one.
TEST(TestWindowGfx, CreateIcon_SVG_NoIntrinsic)
{
  nsCOMPtr<imgIContainer> image;
  LoadImage(SVG_UNSIZED_CIRCLE, getter_AddRefs(image));

  HICON icon;
  nsresult rv =
      nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
                              LayoutDeviceIntSize(200, 200), &icon);
  ASSERT_NS_SUCCEEDED(rv);

  ICONINFO ii;
  BOOL fResult = ::GetIconInfo(icon, &ii);
  ASSERT_TRUE(fResult);

  BITMAP bm;
  fResult = ::GetObject(ii.hbmColor, sizeof(bm), &bm) == sizeof(bm);
  ASSERT_TRUE(fResult);

  ASSERT_EQ(bm.bmWidth, 200);
  ASSERT_EQ(bm.bmHeight, 200);

  double redCount, greenCount, blueCount;
  CountPixels(ii, bm, &redCount, &greenCount, &blueCount);

  // We've scaled the image up to four times its size.
  double fillArea = CIRCLE_FILL_AREA * 4;
  double strokeArea = CIRCLE_STROKE_AREA * 4;

  ASSERT_NEARLY(redCount, strokeArea);
  ASSERT_NEARLY(greenCount, fillArea);
  ASSERT_EQ(blueCount, 0.0);

  if (ii.hbmMask) DeleteObject(ii.hbmMask);
  if (ii.hbmColor) DeleteObject(ii.hbmColor);

  ::DestroyIcon(icon);
}

// Tests that we can set SVG context-fill and context-stroke
TEST(TestWindowGfx, CreateIcon_SVG_Context)
{
  // Normally the context properties don't work for content documents including
  // data URIs.
  Preferences::SetBool("svg.context-properties.content.enabled"true);
  // This test breaks if color management is enabled and an earlier gtest may
  // have enabled it.
  gfxPlatform::SetCMSModeOverride(CMSMode::Off);

  nsCOMPtr<imgIContainer> image;
  LoadImage(SVG_CONTEXT_CIRCLE, getter_AddRefs(image));

  nsCOMPtr<nsISVGPaintContext> paintContext =
      new SvgPaintContext("#00FF00""#0000FF");

  HICON icon;
  nsresult rv = nsWindowGfx::CreateIcon(image, paintContext, false,
                                        LayoutDeviceIntPoint(),
                                        LayoutDeviceIntSize(200, 200), &icon);
  ASSERT_NS_SUCCEEDED(rv);

  ICONINFO ii;
  BOOL fResult = ::GetIconInfo(icon, &ii);
  ASSERT_TRUE(fResult);

  BITMAP bm;
  fResult = ::GetObject(ii.hbmColor, sizeof(bm), &bm) == sizeof(bm);
  ASSERT_TRUE(fResult);

  ASSERT_EQ(bm.bmWidth, 200);
  ASSERT_EQ(bm.bmHeight, 200);

  double redCount, greenCount, blueCount;
  CountPixels(ii, bm, &redCount, &greenCount, &blueCount);

  // We've scaled the image up to four times its size.
  double fillArea = CIRCLE_FILL_AREA * 4;
  double strokeArea = CIRCLE_STROKE_AREA * 4;

  ASSERT_NEARLY(greenCount, strokeArea);
  ASSERT_NEARLY(blueCount, fillArea);
  ASSERT_EQ(redCount, 0.0);

  if (ii.hbmMask) DeleteObject(ii.hbmMask);
  if (ii.hbmColor) DeleteObject(ii.hbmColor);

  ::DestroyIcon(icon);
}

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.