Backed out 4 changesets (bug 1920468, bug 1920674) for causing gtest failures in TestWindowGfx.CreateIcon_SVG_Context. CLOSED TREE

Backed out changeset 51a61309e309 (bug 1920468)
Backed out changeset bd830a73c821 (bug 1920674)
Backed out changeset 03f5f21e011f (bug 1920468)
Backed out changeset a1dac924abab (bug 1920674)
This commit is contained in:
Butkovits Atila 2024-10-17 12:08:51 +03:00
parent 15ff5d04a1
commit af06d4f892
12 changed files with 58 additions and 632 deletions

View File

@ -32,66 +32,6 @@ XPCOMUtils.defineLazyServiceGetter(
const PROFILES_CRYPTO_SALT_LENGTH_BYTES = 16;
function loadImage(url) {
return new Promise((resolve, reject) => {
let imageTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
let imageContainer;
let observer = imageTools.createScriptedObserver({
sizeAvailable() {
resolve(imageContainer);
},
});
imageTools.decodeImageFromChannelAsync(
url,
Services.io.newChannelFromURI(
url,
null,
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
Ci.nsIContentPolicy.TYPE_IMAGE
),
(image, status) => {
if (!Components.isSuccessCode(status)) {
reject(new Components.Exception("Image loading failed", status));
} else {
imageContainer = image;
}
},
observer
);
});
}
async function updateTaskbar(iconUrl, profileName, strokeColor, fillColor) {
try {
let image = await loadImage(iconUrl);
if ("nsIMacDockSupport" in Ci) {
Cc["@mozilla.org/widget/macdocksupport;1"]
.getService(Ci.nsIMacDockSupport)
.setBadgeImage(image, { fillColor, strokeColor });
} else if ("nsIWinTaskbar" in Ci) {
lazy.EveryWindow.registerCallback(
"profiles",
win => {
let iconController = Cc["@mozilla.org/windows-taskbar;1"]
.getService(Ci.nsIWinTaskbar)
.getOverlayIconController(win.docShell);
iconController.setOverlayIcon(image, profileName, {
fillColor,
strokeColor,
});
},
() => {}
);
}
} catch (e) {
console.error(e);
}
}
/**
* The service that manages selectable profiles
*/

View File

@ -7,8 +7,6 @@
#include "nsISupports.idl"
interface imgIContainer;
interface nsIURI;
interface nsISVGPaintContext;
/**
* Starting in Windows 7, applications can display an overlay on the icon in
@ -27,10 +25,6 @@ interface nsITaskbarOverlayIconController : nsISupports
* @param statusDescription The alt text version of the information
* conveyed by the overlay, for accessibility
* purposes.
* @param paintContext Optional context information used when passing an SVG
* image. This allows the caller to set the context-fill
* and context-stroke properties that would normally
* be provided by CSS.
*
* @note The behavior for window groups is managed by Windows.
* - If an overlay icon is set for any window in a window group and another
@ -41,6 +35,5 @@ interface nsITaskbarOverlayIconController : nsISupports
* still available, then that previous overlay is displayed.
*/
void setOverlayIcon(in imgIContainer statusIcon,
in AString statusDescription,
[optional] in nsISVGPaintContext paintContext);
in AString statusDescription);
};

View File

@ -190,7 +190,7 @@ nsresult StatusBarEntry::OnComplete(imgIContainer* aImage) {
RefPtr<StatusBarEntry> kungFuDeathGrip = this;
nsresult rv = nsWindowGfx::CreateIcon(
aImage, nullptr, false, LayoutDeviceIntPoint(),
aImage, false, LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), &mIconData.hIcon);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -100,7 +100,7 @@ TaskbarPreviewButton::SetImage(imgIContainer* img) {
if (img) {
nsresult rv;
rv = nsWindowGfx::CreateIcon(
img, nullptr, false, LayoutDeviceIntPoint(),
img, false, LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon),
&Button().hIcon);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -99,7 +99,7 @@ TaskbarTabPreview::SetIcon(imgIContainer* icon) {
if (icon) {
nsresult rv;
rv = nsWindowGfx::CreateIcon(
icon, nullptr, false, LayoutDeviceIntPoint(),
icon, false, LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -14,8 +14,6 @@
#include "TaskbarPreviewButton.h"
#include "nsWindow.h"
#include "nsWindowGfx.h"
#include "imgLoader.h"
#include "nsISVGPaintContext.h"
namespace mozilla {
namespace widget {
@ -196,8 +194,7 @@ TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState,
NS_IMETHODIMP
TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon,
const nsAString& aStatusDescription,
nsISVGPaintContext* aPaintContext) {
const nsAString& aStatusDescription) {
nsresult rv;
if (aStatusIcon) {
// The image shouldn't be animated
@ -210,7 +207,7 @@ TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon,
HICON hIcon = nullptr;
if (aStatusIcon) {
rv = nsWindowGfx::CreateIcon(
aStatusIcon, aPaintContext, false, LayoutDeviceIntPoint(),
aStatusIcon, false, LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -231,7 +231,7 @@ WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy* aWindow,
if (aSmallIcon) {
HICON hIcon = nullptr;
rv = nsWindowGfx::CreateIcon(
aSmallIcon, nullptr, false, mozilla::LayoutDeviceIntPoint(),
aSmallIcon, false, mozilla::LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);
@ -241,7 +241,7 @@ WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy* aWindow,
if (aBigIcon) {
HICON hIcon = nullptr;
rv = nsWindowGfx::CreateIcon(
aBigIcon, nullptr, false, mozilla::LayoutDeviceIntPoint(),
aBigIcon, false, mozilla::LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -2966,8 +2966,8 @@ static HCURSOR CursorForImage(const nsIWidget::Cursor& aCursor,
LayoutDeviceIntPoint hotspot =
RoundedToInt(CSSIntPoint(aCursor.mHotspotX, aCursor.mHotspotY) * aScale);
HCURSOR cursor;
nsresult rv = nsWindowGfx::CreateIcon(aCursor.mContainer, nullptr, true,
hotspot, layoutSize, &cursor);
nsresult rv = nsWindowGfx::CreateIcon(aCursor.mContainer, true, hotspot,
layoutSize, &cursor);
if (NS_FAILED(rv)) {
return nullptr;
}

View File

@ -34,7 +34,6 @@
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SVGImageContext.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsGfxCIID.h"
#include "gfxContext.h"
@ -45,7 +44,6 @@
#include "nsDebug.h"
#include "WindowRenderer.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "ImageRegion.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/layers/CompositorBridgeParent.h"
@ -470,9 +468,8 @@ LayoutDeviceIntSize nsWindowGfx::GetIconMetrics(IconSizeType aSizeType) {
return LayoutDeviceIntSize(width, height);
}
nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer,
nsISVGPaintContext* aSVGPaintContext,
bool aIsCursor, LayoutDeviceIntPoint aHotspot,
nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer, bool aIsCursor,
LayoutDeviceIntPoint aHotspot,
LayoutDeviceIntSize aScaledSize,
HICON* aIcon) {
MOZ_ASSERT(aHotspot.x >= 0 && aHotspot.y >= 0);
@ -480,117 +477,62 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer,
(aScaledSize.width == 0 && aScaledSize.height == 0));
// Get the image data
RefPtr<SourceSurface> surface;
RefPtr<SourceSurface> surface = aContainer->GetFrame(
imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE);
IntSize frameSize = surface->GetSize();
if (frameSize.IsEmpty()) {
return NS_ERROR_FAILURE;
}
IntSize iconSize(aScaledSize.width, aScaledSize.height);
if (iconSize == IntSize(0, 0)) { // use frame's intrinsic size
iconSize = frameSize;
}
RefPtr<DataSourceSurface> dataSurface;
DataSourceSurface::MappedSurface map;
bool mappedOK;
DataSourceSurface::MappedSurface map;
if (aContainer->GetType() == imgIContainer::TYPE_VECTOR) {
auto scaledSize = aScaledSize.ToUnknownSize();
if (iconSize != frameSize) {
// Scale the surface
dataSurface =
Factory::CreateDataSourceSurface(iconSize, SurfaceFormat::B8G8R8A8);
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map);
NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE);
if (scaledSize.IsEmpty()) {
int32_t width, height;
nsresult rv = aContainer->GetWidth(&width);
NS_ENSURE_SUCCESS(rv, rv);
rv = aContainer->GetHeight(&height);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(width > 0 && height > 0, NS_ERROR_FAILURE);
scaledSize = IntSize(width, height);
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
SurfaceFormat::B8G8R8A8);
if (!dt) {
gfxWarning()
<< "nsWindowGfx::CreatesIcon failed in CreateDrawTargetForData";
return NS_ERROR_OUT_OF_MEMORY;
}
RefPtr<DrawTarget> drawTarget =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
scaledSize, SurfaceFormat::B8G8R8A8);
if (!drawTarget || !drawTarget->IsValid()) {
NS_ERROR("Failed to create valid DrawTarget");
return NS_ERROR_FAILURE;
}
gfxContext context(drawTarget);
SVGImageContext svgContext;
svgContext.SetViewportSize(
Some(CSSIntSize(scaledSize.width, scaledSize.height)));
svgContext.SetColorScheme(Some(LookAndFeel::SystemColorScheme()));
SVGImageContext::MaybeStoreContextPaint(svgContext, aSVGPaintContext,
aContainer);
mozilla::image::ImgDrawResult res = aContainer->Draw(
&context, scaledSize, image::ImageRegion::Create(scaledSize),
imgIContainer::FRAME_CURRENT, SamplingFilter::POINT, svgContext,
imgIContainer::FLAG_SYNC_DECODE, 1.0);
if (res != mozilla::image::ImgDrawResult::SUCCESS) {
return NS_ERROR_FAILURE;
}
surface = drawTarget->Snapshot();
NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE);
dataSurface = surface->GetDataSurface();
dt->DrawSurface(surface, Rect(0, 0, iconSize.width, iconSize.height),
Rect(0, 0, frameSize.width, frameSize.height),
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
} else if (surface->GetFormat() != SurfaceFormat::B8G8R8A8) {
// Convert format to SurfaceFormat::B8G8R8A8
dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
surface, SurfaceFormat::B8G8R8A8);
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
} else {
surface = aContainer->GetFrame(
imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
NS_ENSURE_TRUE(surface, NS_ERROR_NOT_AVAILABLE);
IntSize frameSize = surface->GetSize();
if (frameSize.IsEmpty()) {
return NS_ERROR_FAILURE;
}
IntSize iconSize(aScaledSize.width, aScaledSize.height);
if (iconSize == IntSize(0, 0)) { // use frame's intrinsic size
iconSize = frameSize;
}
if (iconSize != frameSize) {
// Scale the surface
dataSurface =
Factory::CreateDataSourceSurface(iconSize, SurfaceFormat::B8G8R8A8);
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map);
NS_ENSURE_TRUE(mappedOK, NS_ERROR_FAILURE);
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
SurfaceFormat::B8G8R8A8);
if (!dt) {
gfxWarning()
<< "nsWindowGfx::CreatesIcon failed in CreateDrawTargetForData";
return NS_ERROR_OUT_OF_MEMORY;
}
dt->DrawSurface(surface, Rect(0, 0, iconSize.width, iconSize.height),
Rect(0, 0, frameSize.width, frameSize.height),
DrawSurfaceOptions(),
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
} else if (surface->GetFormat() != SurfaceFormat::B8G8R8A8) {
// Convert format to SurfaceFormat::B8G8R8A8
dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
surface, SurfaceFormat::B8G8R8A8);
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
} else {
dataSurface = surface->GetDataSurface();
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
}
dataSurface = surface->GetDataSurface();
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
}
NS_ENSURE_TRUE(dataSurface && mappedOK, NS_ERROR_FAILURE);
MOZ_ASSERT(dataSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
IntSize surfaceSize = surface->GetSize();
uint8_t* data = nullptr;
UniquePtr<uint8_t[]> autoDeleteArray;
if (map.mStride ==
BytesPerPixel(dataSurface->GetFormat()) * surfaceSize.width) {
if (map.mStride == BytesPerPixel(dataSurface->GetFormat()) * iconSize.width) {
// Mapped data is already packed
data = map.mData;
} else {
@ -607,9 +549,8 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer,
NS_ENSURE_TRUE(data, NS_ERROR_FAILURE);
}
HBITMAP bmp = DataToBitmap(data, surfaceSize.width, -surfaceSize.height, 32);
uint8_t* a1data =
Data32BitTo1Bit(data, surfaceSize.width, surfaceSize.height);
HBITMAP bmp = DataToBitmap(data, iconSize.width, -iconSize.height, 32);
uint8_t* a1data = Data32BitTo1Bit(data, iconSize.width, iconSize.height);
if (map.mData) {
dataSurface->Unmap();
}
@ -617,8 +558,7 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer* aContainer,
return NS_ERROR_FAILURE;
}
HBITMAP mbmp =
DataToBitmap(a1data, surfaceSize.width, -surfaceSize.height, 1);
HBITMAP mbmp = DataToBitmap(a1data, iconSize.width, -iconSize.height, 1);
free(a1data);
ICONINFO info = {0};

View File

@ -13,25 +13,11 @@
#include "nsWindow.h"
#include <imgIContainer.h>
class nsISVGPaintContext;
class nsWindowGfx {
public:
enum IconSizeType { kSmallIcon, kRegularIcon };
static mozilla::LayoutDeviceIntSize GetIconMetrics(IconSizeType aSizeType);
/**
* Renders an imgIContainer to a HICON.
* aContainer - the image to render.
* aSVGContext - Optional CSS context properties to apply. Ignored if the
* container is not an SVG image.
* aIsCursor - true if this icon will be used as a mouse cursor.
* aHotSpot - the position of the hot spot for a mouse cursor.
* aScaledSize - the size of the icon to generate.
* aIcon - Out parameter for the returned HICON. Required.
*/
static nsresult CreateIcon(imgIContainer* aContainer,
nsISVGPaintContext* aSVGContext, bool aIsCursor,
static nsresult CreateIcon(imgIContainer* aContainer, bool aIsCursor,
mozilla::LayoutDeviceIntPoint aHotspot,
mozilla::LayoutDeviceIntSize aScaledSize,
HICON* aIcon);

View File

@ -1,427 +0,0 @@
/* -*- 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 "mozilla/Base64.h"
#include "mozilla/Encoding.h"
#include "mozilla/Preferences.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/SystemPrincipal.h"
#include "mozilla/UniquePtr.h"
#include "nsILoadInfo.h"
#include "nsISVGPaintContext.h"
#include "nsMimeTypes.h"
#include "nsStreamUtils.h"
#include "nsWindowGfx.h"
#include "gtest/gtest.h"
using namespace mozilla::image;
const char* SVG_GREEN_CIRCLE =
"<?xml version=\"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 =
"<?xml version=\"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 =
"<?xml version=\"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_TRUE(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_TRUE(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_TRUE(NS_SUCCEEDED(rv));
uint64_t size;
rv = stream->Available(&size);
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_EQ(size, strlen(aData));
rv = image->OnImageDataAvailable(channel, stream, 0, size);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Let the Image know we've sent all the data.
rv = image->OnImageDataComplete(channel, NS_OK, true);
ASSERT_TRUE(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 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 SVG
TEST(TestWindowGfx, CreateIcon_SVG_ScaledDown)
{
nsCOMPtr<imgIContainer> image;
LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(image));
HICON icon;
nsresult rv =
nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
LayoutDeviceIntSize(50, 50), &icon);
ASSERT_TRUE(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);
}
// Tests that we can scale up an SVG
TEST(TestWindowGfx, CreateIcon_SVG_ScaledUp)
{
nsCOMPtr<imgIContainer> image;
LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(image));
HICON icon;
nsresult rv =
nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
LayoutDeviceIntSize(200, 200), &icon);
ASSERT_TRUE(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 render an SVG at its intrinsic size
TEST(TestWindowGfx, CreateIcon_SVG_Intrinsic)
{
nsCOMPtr<imgIContainer> image;
LoadImage(SVG_GREEN_CIRCLE, getter_AddRefs(image));
HICON icon;
nsresult rv =
nsWindowGfx::CreateIcon(image, nullptr, false, LayoutDeviceIntPoint(),
LayoutDeviceIntSize(), &icon);
ASSERT_TRUE(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);
}
// If the 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_TRUE(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);
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_TRUE(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);
}

View File

@ -7,13 +7,10 @@
UNIFIED_SOURCES += [
"TestJumpListBuilder.cpp",
"TestWinDND.cpp",
"TestWindowGfx.cpp",
"TestWinUtils.cpp",
]
LOCAL_INCLUDES += [
"/gfx/cairo/cairo/src",
"/image",
"/widget",
"/widget/windows",
]