Commit a73fc377 authored by vitalybuka's avatar vitalybuka Committed by Commit bot

Removed unused code in skia/ext/vector_*.

Code is not used after we Chrome stopped generation of EMF inside render.

Review URL: https://codereview.chromium.org/900683004

Cr-Commit-Position: refs/heads/master@{#315076}
parent 0071593b
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "printing/page_size_margins.h" #include "printing/page_size_margins.h"
#include "printing/pdf_metafile_skia.h" #include "printing/pdf_metafile_skia.h"
#include "skia/ext/platform_device.h" #include "skia/ext/platform_device.h"
#include "skia/ext/vector_canvas.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebLocalFrame.h"
#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
...@@ -162,7 +161,7 @@ void PrintWebViewHelper::PrintPageInternal( ...@@ -162,7 +161,7 @@ void PrintWebViewHelper::PrintPageInternal(
gfx::Rect canvas_area = gfx::Rect canvas_area =
params.params.display_header_footer ? gfx::Rect(page_size) : content_area; params.params.display_header_footer ? gfx::Rect(page_size) : content_area;
skia::VectorCanvas* canvas = skia::PlatformCanvas* canvas =
metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor);
if (!canvas) if (!canvas)
return; return;
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "printing/metafile_skia_wrapper.h" #include "printing/metafile_skia_wrapper.h"
#include "printing/page_size_margins.h" #include "printing/page_size_margins.h"
#include "skia/ext/platform_device.h" #include "skia/ext/platform_device.h"
#include "skia/ext/vector_canvas.h"
#include "third_party/WebKit/public/platform/WebCanvas.h" #include "third_party/WebKit/public/platform/WebCanvas.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebLocalFrame.h"
...@@ -118,7 +117,7 @@ void PrintWebViewHelper::RenderPage(const PrintMsg_Print_Params& params, ...@@ -118,7 +117,7 @@ void PrintWebViewHelper::RenderPage(const PrintMsg_Print_Params& params,
params.display_header_footer ? gfx::Rect(*page_size) : content_area; params.display_header_footer ? gfx::Rect(*page_size) : content_area;
{ {
skia::VectorCanvas* canvas = metafile->GetVectorCanvasForNewPage( skia::PlatformCanvas* canvas = metafile->GetVectorCanvasForNewPage(
*page_size, canvas_area, scale_factor); *page_size, canvas_area, scale_factor);
if (!canvas) if (!canvas)
return; return;
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "printing/pdf_metafile_skia.h" #include "printing/pdf_metafile_skia.h"
#include "printing/units.h" #include "printing/units.h"
#include "skia/ext/platform_device.h" #include "skia/ext/platform_device.h"
#include "skia/ext/vector_canvas.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebLocalFrame.h"
namespace printing { namespace printing {
...@@ -178,7 +177,7 @@ void PrintWebViewHelper::PrintPageInternal( ...@@ -178,7 +177,7 @@ void PrintWebViewHelper::PrintPageInternal(
frame->getPrintPageShrink(params.page_number); frame->getPrintPageShrink(params.page_number);
float scale_factor = css_scale_factor * webkit_page_shrink_factor; float scale_factor = css_scale_factor * webkit_page_shrink_factor;
skia::VectorCanvas* canvas = skia::PlatformCanvas* canvas =
metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor); metafile->GetVectorCanvasForNewPage(page_size, canvas_area, scale_factor);
if (!canvas) if (!canvas)
return; return;
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "content/renderer/render_thread_impl.h" #include "content/renderer/render_thread_impl.h"
#include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox.h"
#include "skia/ext/fontmgr_default_win.h" #include "skia/ext/fontmgr_default_win.h"
#include "skia/ext/vector_platform_device_emf_win.h"
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
#include "third_party/WebKit/public/web/win/WebFontRendering.h" #include "third_party/WebKit/public/web/win/WebFontRendering.h"
#include "third_party/icu/source/i18n/unicode/timezone.h" #include "third_party/icu/source/i18n/unicode/timezone.h"
...@@ -101,8 +100,6 @@ void RendererMainPlatformDelegate::PlatformInitialize() { ...@@ -101,8 +100,6 @@ void RendererMainPlatformDelegate::PlatformInitialize() {
WarmupDirectWrite(); WarmupDirectWrite();
} else { } else {
SkTypeface_SetEnsureLOGFONTAccessibleProc(SkiaPreCacheFont); SkTypeface_SetEnsureLOGFONTAccessibleProc(SkiaPreCacheFont);
skia::SetSkiaEnsureTypefaceCharactersAccessible(
SkiaPreCacheFontCharacters);
} }
} }
blink::WebFontRendering::setUseDirectWrite(use_direct_write); blink::WebFontRendering::setUseDirectWrite(use_direct_write);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "base/win/scoped_gdi_object.h" #include "base/win/scoped_gdi_object.h"
#include "base/win/scoped_hdc.h" #include "base/win/scoped_hdc.h"
#include "base/win/scoped_select_object.h" #include "base/win/scoped_select_object.h"
#include "skia/ext/vector_platform_device_emf_win.h" #include "skia/ext/platform_device.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/codec/png_codec.h"
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "skia/ext/refptr.h" #include "skia/ext/refptr.h"
#include "skia/ext/vector_canvas.h"
#include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkDocument.h" #include "third_party/skia/include/core/SkDocument.h"
#include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkPictureRecorder.h"
...@@ -104,7 +103,7 @@ bool PdfMetafileSkia::StartPage(const gfx::Size& page_size, ...@@ -104,7 +103,7 @@ bool PdfMetafileSkia::StartPage(const gfx::Size& page_size,
return true; return true;
} }
skia::VectorCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage( skia::PlatformCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
const gfx::Size& page_size, const gfx::Size& page_size,
const gfx::Rect& content_area, const gfx::Rect& content_area,
const float& scale_factor) { const float& scale_factor) {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "printing/metafile.h" #include "printing/metafile.h"
#include "skia/ext/vector_canvas.h" #include "skia/ext/platform_canvas.h"
#if defined(OS_WIN) #if defined(OS_WIN)
#include <windows.h> #include <windows.h>
...@@ -74,12 +74,12 @@ class PRINTING_EXPORT PdfMetafileSkia : public Metafile { ...@@ -74,12 +74,12 @@ class PRINTING_EXPORT PdfMetafileSkia : public Metafile {
scoped_ptr<PdfMetafileSkia> GetMetafileForCurrentPage(); scoped_ptr<PdfMetafileSkia> GetMetafileForCurrentPage();
// This method calls StartPage and then returns an appropriate // This method calls StartPage and then returns an appropriate
// VectorCanvas implementation bound to the context created by // PlatformCanvas implementation bound to the context created by
// StartPage or NULL on error. The skia::VectorCanvas pointer that // StartPage or NULL on error. The skia::PlatformCanvas pointer that
// is returned is owned by this PdfMetafileSkia object and does not // is returned is owned by this PdfMetafileSkia object and does not
// need to be ref()ed or unref()ed. The canvas will remain valid // need to be ref()ed or unref()ed. The canvas will remain valid
// until FinishPage() or FinishDocument() is called. // until FinishPage() or FinishDocument() is called.
skia::VectorCanvas* GetVectorCanvasForNewPage(const gfx::Size& page_size, skia::PlatformCanvas* GetVectorCanvasForNewPage(const gfx::Size& page_size,
const gfx::Rect& content_area, const gfx::Rect& content_area,
const float& scale_factor); const float& scale_factor);
......
...@@ -332,9 +332,6 @@ component("skia") { ...@@ -332,9 +332,6 @@ component("skia") {
"ext/skia_utils_mac.h", "ext/skia_utils_mac.h",
"ext/skia_utils_win.cc", "ext/skia_utils_win.cc",
"ext/skia_utils_win.h", "ext/skia_utils_win.h",
"ext/vector_canvas.h",
"ext/vector_platform_device_emf_win.cc",
"ext/vector_platform_device_emf_win.h",
] ]
# The skia gypi values are relative to the skia_dir, so we need to rebase. # The skia gypi values are relative to the skia_dir, so we need to rebase.
...@@ -646,13 +643,8 @@ test("skia_unittests") { ...@@ -646,13 +643,8 @@ test("skia_unittests") {
"ext/refptr_unittest.cc", "ext/refptr_unittest.cc",
"ext/skia_utils_ios_unittest.mm", "ext/skia_utils_ios_unittest.mm",
"ext/skia_utils_mac_unittest.mm", "ext/skia_utils_mac_unittest.mm",
"ext/vector_canvas_unittest.cc",
] ]
if (!is_win) {
sources -= [ "ext/vector_canvas_unittest.cc" ]
}
if (!is_win && !is_mac) { if (!is_win && !is_mac) {
sources -= [ "ext/platform_canvas_unittest.cc" ] sources -= [ "ext/platform_canvas_unittest.cc" ]
} }
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SKIA_EXT_VECTOR_CANVAS_H_
#define SKIA_EXT_VECTOR_CANVAS_H_
#include "base/compiler_specific.h"
#include "skia/ext/platform_canvas.h"
// This was a specialization of PlatformCanvas, but all necessary functionality
// has been subsumed by just SkCanvas and a specialized device (PDF or EMF).
// Future evolution goal is to replace this notion (canvas+device) with
// an updated version of SkDocument, which will have explicit APIs for margins.
// At that point, this class (and header) will be removed entirely.
namespace skia {
typedef PlatformCanvas VectorCanvas;
} // namespace skia
#endif // SKIA_EXT_VECTOR_CANVAS_H_
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#if !defined(OS_WIN)
#include <unistd.h>
#endif
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "skia/ext/vector_canvas.h"
#include "skia/ext/vector_platform_device_emf_win.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkDashPathEffect.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
namespace skia {
namespace {
const char kGenerateSwitch[] = "vector-canvas-generate";
// Lightweight HDC management.
class Context {
public:
Context() : context_(CreateCompatibleDC(NULL)) {
EXPECT_TRUE(context_);
}
~Context() {
DeleteDC(context_);
}
HDC context() const { return context_; }
private:
HDC context_;
DISALLOW_COPY_AND_ASSIGN(Context);
};
// Lightweight HBITMAP management.
class Bitmap {
public:
Bitmap(const Context& context, int x, int y) {
BITMAPINFOHEADER hdr;
hdr.biSize = sizeof(BITMAPINFOHEADER);
hdr.biWidth = x;
hdr.biHeight = -y; // Minus means top-down bitmap.
hdr.biPlanes = 1;
hdr.biBitCount = 32;
hdr.biCompression = BI_RGB; // No compression.
hdr.biSizeImage = 0;
hdr.biXPelsPerMeter = 1;
hdr.biYPelsPerMeter = 1;
hdr.biClrUsed = 0;
hdr.biClrImportant = 0;
bitmap_ = CreateDIBSection(context.context(),
reinterpret_cast<BITMAPINFO*>(&hdr), 0,
&data_, NULL, 0);
EXPECT_TRUE(bitmap_);
EXPECT_TRUE(SelectObject(context.context(), bitmap_));
}
~Bitmap() {
EXPECT_TRUE(DeleteObject(bitmap_));
}
private:
HBITMAP bitmap_;
void* data_;
DISALLOW_COPY_AND_ASSIGN(Bitmap);
};
// Lightweight raw-bitmap management. The image, once initialized, is immuable.
// It is mainly used for comparison.
class Image {
public:
// Creates the image from the given filename on disk.
explicit Image(const base::FilePath& filename) : ignore_alpha_(true) {
std::string compressed;
base::ReadFileToString(filename, &compressed);
EXPECT_TRUE(compressed.size());
SkBitmap bitmap;
EXPECT_TRUE(gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(compressed.data()),
compressed.size(), &bitmap));
SetSkBitmap(bitmap);
}
// Loads the image from a canvas.
Image(skia::PlatformCanvas& canvas) : ignore_alpha_(true) {
// Use a different way to access the bitmap. The normal way would be to
// query the SkBitmap.
skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
HDC context = scoped_platform_paint.GetPlatformSurface();
HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
EXPECT_TRUE(bitmap != NULL);
// Initialize the clip region to the entire bitmap.
BITMAP bitmap_data;
EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data), sizeof(BITMAP));
width_ = bitmap_data.bmWidth;
height_ = bitmap_data.bmHeight;
row_length_ = bitmap_data.bmWidthBytes;
size_t size = row_length_ * height_;
data_.resize(size);
memcpy(&*data_.begin(), bitmap_data.bmBits, size);
}
// Loads the image from a canvas.
Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
SetSkBitmap(bitmap);
}
int width() const { return width_; }
int height() const { return height_; }
int row_length() const { return row_length_; }
// Save the image to a png file. Used to create the initial test files.
void SaveToFile(const base::FilePath& filename) {
std::vector<unsigned char> compressed;
ASSERT_TRUE(gfx::PNGCodec::Encode(&*data_.begin(),
gfx::PNGCodec::FORMAT_BGRA,
gfx::Size(width_, height_),
row_length_,
true,
std::vector<gfx::PNGCodec::Comment>(),
&compressed));
ASSERT_TRUE(compressed.size());
FILE* f = base::OpenFile(filename, "wb");
ASSERT_TRUE(f);
ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
compressed.size());
base::CloseFile(f);
}
// Returns the percentage of the image that is different from the other,
// between 0 and 100.
double PercentageDifferent(const Image& rhs) const {
if (width_ != rhs.width_ ||
height_ != rhs.height_ ||
row_length_ != rhs.row_length_ ||
width_ == 0 ||
height_ == 0) {
return 100.; // When of different size or empty, they are 100% different.
}
// Compute pixels different in the overlap
int pixels_different = 0;
for (int y = 0; y < height_; ++y) {
for (int x = 0; x < width_; ++x) {
uint32_t lhs_pixel = pixel_at(x, y);
uint32_t rhs_pixel = rhs.pixel_at(x, y);
if (lhs_pixel != rhs_pixel)
++pixels_different;
}
}
// Like the WebKit ImageDiff tool, we define percentage different in terms
// of the size of the 'actual' bitmap.
double total_pixels = static_cast<double>(width_) *
static_cast<double>(height_);
return static_cast<double>(pixels_different) / total_pixels * 100.;
}
// Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
// depending on ignore_alpha_.
uint32 pixel_at(int x, int y) const {
EXPECT_TRUE(x >= 0 && x < width_);
EXPECT_TRUE(y >= 0 && y < height_);
const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
const uint32* data_row = data + y * row_length_ / sizeof(uint32);
if (ignore_alpha_)
return data_row[x] & 0xFFFFFF; // Strip out A.
else
return data_row[x];
}
protected:
void SetSkBitmap(const SkBitmap& bitmap) {
SkAutoLockPixels lock(bitmap);
width_ = bitmap.width();
height_ = bitmap.height();
row_length_ = static_cast<int>(bitmap.rowBytes());
size_t size = row_length_ * height_;
data_.resize(size);
memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
}
private:
// Pixel dimensions of the image.
int width_;
int height_;
// Length of a line in bytes.
int row_length_;
// Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
// 0xABGR).
std::vector<unsigned char> data_;
// Flag to signal if the comparison functions should ignore the alpha channel.
const bool ignore_alpha_;
DISALLOW_COPY_AND_ASSIGN(Image);
};
// Base for tests. Capability to process an image.
class ImageTest : public testing::Test {
public:
// In what state is the test running.
enum ProcessAction {
GENERATE,
COMPARE,
NOOP,
};
ImageTest(ProcessAction default_action)
: action_(default_action) {
}
protected:
virtual void SetUp() {
const testing::TestInfo& test_info =
*testing::UnitTest::GetInstance()->current_test_info();
PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
test_dir_ = test_dir_.AppendASCII("skia").
AppendASCII("ext").
AppendASCII("data").
AppendASCII(test_info.test_case_name()).
AppendASCII(test_info.name());
// Hack for a quick lowercase. We assume all the tests names are ASCII.
base::FilePath::StringType tmp(test_dir_.value());
for (size_t i = 0; i < tmp.size(); ++i)
tmp[i] = base::ToLowerASCII(tmp[i]);
test_dir_ = base::FilePath(tmp);
if (action_ == GENERATE) {
// Make sure the directory exist.
base::CreateDirectory(test_dir_);
}
}
// Returns the fully qualified path of a data file.
base::FilePath test_file(const base::FilePath::StringType& filename) const {
// Hack for a quick lowercase. We assume all the test data file names are
// ASCII.
#if defined(OS_WIN)
std::string tmp = base::UTF16ToASCII(filename);
#else
std::string tmp(filename);
#endif
for (size_t i = 0; i < tmp.size(); ++i)
tmp[i] = base::ToLowerASCII(tmp[i]);
return test_dir_.AppendASCII(tmp);
}
// Compares or saves the bitmap currently loaded in the context, depending on
// kGenerating value. Returns 0 on success or any positive value between ]0,
// 100] on failure. The return value is the percentage of difference between
// the image in the file and the image in the canvas.
double ProcessCanvas(skia::PlatformCanvas& canvas,
base::FilePath::StringType filename) const {
filename = filename + FILE_PATH_LITERAL(".png");
switch (action_) {
case GENERATE:
SaveImage(canvas, filename);
return 0.;
case COMPARE:
return CompareImage(canvas, filename);
case NOOP:
return 0;
default:
// Invalid state, returns that the image is 100 different.
return 100.;
}
}
// Compares the bitmap currently loaded in the context with the file. Returns
// the percentage of pixel difference between both images, between 0 and 100.
double CompareImage(skia::PlatformCanvas& canvas,
const base::FilePath::StringType& filename) const {
Image image1(canvas);
Image image2(test_file(filename));
double diff = image1.PercentageDifferent(image2);
return diff;
}
// Saves the bitmap currently loaded in the context into the file.
void SaveImage(skia::PlatformCanvas& canvas,
const base::FilePath::StringType& filename) const {
Image(canvas).SaveToFile(test_file(filename));
}
ProcessAction action_;
// Path to directory used to contain the test data.
base::FilePath test_dir_;
DISALLOW_COPY_AND_ASSIGN(ImageTest);
};
// Premultiply the Alpha channel on the R, B and G channels.
void Premultiply(SkBitmap bitmap) {
SkAutoLockPixels lock(bitmap);
for (int x = 0; x < bitmap.width(); ++x) {
for (int y = 0; y < bitmap.height(); ++y) {
uint32_t* pixel_addr = bitmap.getAddr32(x, y);
uint32_t color = *pixel_addr;
BYTE alpha = SkColorGetA(color);
if (!alpha) {
*pixel_addr = 0;
} else {
BYTE alpha_offset = alpha / 2;
*pixel_addr = SkColorSetARGB(
SkColorGetA(color),
(SkColorGetR(color) * 255 + alpha_offset) / alpha,
(SkColorGetG(color) * 255 + alpha_offset) / alpha,
(SkColorGetB(color) * 255 + alpha_offset) / alpha);
}
}
}
}
void LoadPngFileToSkBitmap(const base::FilePath& filename,
SkBitmap* bitmap,
bool is_opaque) {
std::string compressed;
base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed);
ASSERT_TRUE(compressed.size());
ASSERT_TRUE(gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(compressed.data()),
compressed.size(), bitmap));
EXPECT_EQ(is_opaque, bitmap->isOpaque());
Premultiply(*bitmap);
}
} // namespace
// Streams an image.
inline std::ostream& operator<<(std::ostream& out, const Image& image) {
return out << "Image(" << image.width() << ", "
<< image.height() << ", " << image.row_length() << ")";
}
// Runs simultaneously the same drawing commands on VectorCanvas and
// PlatformCanvas and compare the results.
class VectorCanvasTest : public ImageTest {
public:
typedef ImageTest parent;
VectorCanvasTest() : parent(CurrentMode()), compare_canvas_(true) {
}
protected:
virtual void SetUp() {
parent::SetUp();
Init(100);
number_ = 0;
}
virtual void TearDown() {
delete pcanvas_;
pcanvas_ = NULL;
delete vcanvas_;
vcanvas_ = NULL;
delete bitmap_;
bitmap_ = NULL;
delete context_;
context_ = NULL;
parent::TearDown();
}
void Init(int size) {
size_ = size;
context_ = new Context();
bitmap_ = new Bitmap(*context_, size_, size_);
vcanvas_ = new VectorCanvas(
VectorPlatformDeviceEmf::CreateDevice(
size_, size_, true, context_->context()));
pcanvas_ = CreatePlatformCanvas(size_, size_, false);
// Clear white.
vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
}
// Compares both canvas and returns the pixel difference in percentage between
// both images. 0 on success and ]0, 100] on failure.
double ProcessImage(const base::FilePath::StringType& filename) {
std::wstring number(base::StringPrintf(L"%02d_", number_++));
double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
if (!compare_canvas_)
return std::max(diff1, diff2);
Image image1(*vcanvas_);
Image image2(*pcanvas_);
double diff = image1.PercentageDifferent(image2);
return std::max(std::max(diff1, diff2), diff);
}
// Returns COMPARE, which is the default. If kGenerateSwitch command
// line argument is used to start this process, GENERATE is returned instead.
static ProcessAction CurrentMode() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch)
? GENERATE
: COMPARE;
}
// Length in x and y of the square canvas.
int size_;
// Current image number in the current test. Used to number of test files.
int number_;
// A temporary HDC to draw into.
Context* context_;
// Bitmap created inside context_.
Bitmap* bitmap_;
// Vector based canvas.
VectorCanvas* vcanvas_;
// Pixel based canvas.
PlatformCanvas* pcanvas_;
// When true (default), vcanvas_ and pcanvas_ contents are compared and
// verified to be identical.
bool compare_canvas_;
};
////////////////////////////////////////////////////////////////////////////////
// Actual tests
#if !defined(USE_AURA) // http://crbug.com/154358
TEST_F(VectorCanvasTest, BasicDrawing) {
EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
<< L"clean";
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clean")));
// Clear white.
{
vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawARGB")));
// Diagonal line top-left to bottom-right.
{
SkPaint paint;
// Default color is black.
vcanvas_->drawLine(10, 10, 90, 90, paint);
pcanvas_->drawLine(10, 10, 90, 90, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_black")));
// Rect.
{
SkPaint paint;
paint.setColor(SK_ColorGREEN);
vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_green")));
// A single-point rect doesn't leave any mark.
{
SkPaint paint;
paint.setColor(SK_ColorBLUE);
vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
// Rect.
{
SkPaint paint;
paint.setColor(SK_ColorBLUE);
vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
// Empty again
{
vcanvas_->drawPaint(SkPaint());
pcanvas_->drawPaint(SkPaint());
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPaint_black")));
// Horizontal line left to right.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(10, 20, 90, 20, paint);
pcanvas_->drawLine(10, 20, 90, 20, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_left_to_right")));
// Vertical line downward.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(30, 10, 30, 90, paint);
pcanvas_->drawLine(30, 10, 30, 90, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_red")));
}
TEST_F(VectorCanvasTest, Circles) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Stroked Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 75, 10);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorMAGENTA);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke")));
// Filled Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 25, 10);
paint.setStyle(SkPaint::kFill_Style);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_fill")));
// Stroked Circle over.
{
SkPaint paint;
SkPath path;
path.addCircle(50, 25, 10);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorBLUE);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_over_strike")));
// Stroke and Fill Circle.
{
SkPaint paint;
SkPath path;
path.addCircle(12, 50, 10);
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setColor(SK_ColorRED);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke_and_fill")));
// Line + Quad + Cubic.
{
SkPaint paint;
SkPath path;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorGREEN);
path.moveTo(1, 1);
path.lineTo(60, 40);
path.lineTo(80, 80);
path.quadTo(20, 50, 10, 90);
path.quadTo(50, 20, 90, 10);
path.cubicTo(20, 40, 50, 50, 10, 10);
path.cubicTo(30, 20, 50, 50, 90, 10);
path.addRect(90, 90, 95, 96);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("mixed_stroke")));
}
TEST_F(VectorCanvasTest, LineOrientation) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Horizontal lines.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
// Left to right.
vcanvas_->drawLine(10, 20, 90, 20, paint);
pcanvas_->drawLine(10, 20, 90, 20, paint);
// Right to left.
vcanvas_->drawLine(90, 30, 10, 30, paint);
pcanvas_->drawLine(90, 30, 10, 30, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal")));
// Vertical lines.
{
SkPaint paint;
paint.setColor(SK_ColorRED);
// Top down.
vcanvas_->drawLine(20, 10, 20, 90, paint);
pcanvas_->drawLine(20, 10, 20, 90, paint);
// Bottom up.
vcanvas_->drawLine(30, 90, 30, 10, paint);
pcanvas_->drawLine(30, 90, 30, 10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical")));
// Try again with a 180 degres rotation.
vcanvas_->rotate(180);
pcanvas_->rotate(180);
// Horizontal lines (rotated).
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(-10, -25, -90, -25, paint);
pcanvas_->drawLine(-10, -25, -90, -25, paint);
vcanvas_->drawLine(-90, -35, -10, -35, paint);
pcanvas_->drawLine(-90, -35, -10, -35, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal_180")));
// Vertical lines (rotated).
{
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(-25, -10, -25, -90, paint);
pcanvas_->drawLine(-25, -10, -25, -90, paint);
vcanvas_->drawLine(-35, -90, -35, -10, paint);
pcanvas_->drawLine(-35, -90, -35, -10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical_180")));
}
TEST_F(VectorCanvasTest, PathOrientation) {
// There is NO WAY to make them agree. At least verify that the output doesn't
// change across versions. This test is disabled. See bug 1060231.
compare_canvas_ = false;
// Horizontal lines.
{
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorRED);
SkPath path;
SkPoint start;
start.set(10, 20);
SkPoint end;
end.set(90, 20);
path.moveTo(start);
path.lineTo(end);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_ltr")));
// Horizontal lines.
{
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorRED);
SkPath path;
SkPoint start;
start.set(90, 30);
SkPoint end;
end.set(10, 30);
path.moveTo(start);
path.lineTo(end);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_rtl")));
}
TEST_F(VectorCanvasTest, DiagonalLines) {
SkPaint paint;
paint.setColor(SK_ColorRED);
vcanvas_->drawLine(10, 10, 90, 90, paint);
pcanvas_->drawLine(10, 10, 90, 90, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("nw-se")));
// Starting here, there is NO WAY to make them agree. At least verify that the
// output doesn't change across versions. This test is disabled. See bug
// 1060231.
compare_canvas_ = false;
vcanvas_->drawLine(10, 95, 90, 15, paint);
pcanvas_->drawLine(10, 95, 90, 15, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("sw-ne")));
vcanvas_->drawLine(90, 10, 10, 90, paint);
pcanvas_->drawLine(90, 10, 10, 90, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("ne-sw")));
vcanvas_->drawLine(95, 90, 15, 10, paint);
pcanvas_->drawLine(95, 90, 15, 10, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("se-nw")));
}
#if defined(OS_WIN)
#define MAYBE_PathEffects DISABLED_PathEffects
#else
#define MAYBE_PathEffects PathEffects
#endif
TEST_F(VectorCanvasTest, MAYBE_PathEffects) {
{
SkPaint paint;
SkScalar intervals[] = { 1, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
vcanvas_->drawLine(10, 10, 90, 10, paint);
pcanvas_->drawLine(10, 10, 90, 10, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_line")));
// Starting here, there is NO WAY to make them agree. At least verify that the
// output doesn't change across versions. This test is disabled. See bug
// 1060231.
compare_canvas_ = false;
{
SkPaint paint;
SkScalar intervals[] = { 3, 5 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.moveTo(10, 15);
path.lineTo(90, 15);
path.lineTo(90, 90);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_path")));
{
SkPaint paint;
SkScalar intervals[] = { 2, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
}
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_rect")));
// This thing looks like it has been drawn by a 3 years old kid. I haven't
// filed a bug on this since I guess nobody is expecting this to look nice.
{
SkPaint paint;
SkScalar intervals[] = { 1, 1 };
skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
new SkDashPathEffect(intervals, arraysize(intervals), 0));
paint.setPathEffect(effect.get());
paint.setColor(SK_ColorMAGENTA);
paint.setStyle(SkPaint::kStroke_Style);
SkPath path;
path.addCircle(50, 75, 10);
vcanvas_->drawPath(path, paint);
pcanvas_->drawPath(path, paint);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle")));
}
}
TEST_F(VectorCanvasTest, Bitmaps) {
{
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap, true);
vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("opaque")));
}
{
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap, false);
vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("alpha")));
}
}
TEST_F(VectorCanvasTest, ClippingRect) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rect")));
}
TEST_F(VectorCanvasTest, ClippingPath) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkPath path;
path.addCircle(20, 20, 10);
vcanvas_->clipPath(path);
pcanvas_->clipPath(path);
vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("path")));
}
TEST_F(VectorCanvasTest, ClippingCombined) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
SkPath path;
path.addCircle(20, 20, 10);
vcanvas_->clipPath(path, SkRegion::kUnion_Op);
pcanvas_->clipPath(path, SkRegion::kUnion_Op);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("combined")));
}
TEST_F(VectorCanvasTest, ClippingIntersect) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
SkPath path;
path.addCircle(23, 23, 15);
vcanvas_->clipPath(path);
pcanvas_->clipPath(path);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("intersect")));
}
TEST_F(VectorCanvasTest, ClippingClean) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
{
SkAutoCanvasRestore acrv(vcanvas_, true);
SkAutoCanvasRestore acrp(pcanvas_, true);
SkRect rect;
rect.fLeft = 2;
rect.fTop = 2;
rect.fRight = 30.5f;
rect.fBottom = 30.5f;
vcanvas_->clipRect(rect);
pcanvas_->clipRect(rect);
vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clipped")));
}
{
// Verify that the clipping region has been fixed back.
vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("unclipped")));
}
}
// See http://crbug.com/26938
TEST_F(VectorCanvasTest, DISABLED_Matrix) {
SkBitmap bitmap;
LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
true);
{
vcanvas_->translate(15, 3);
pcanvas_->translate(15, 3);
vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate1")));
}
{
vcanvas_->translate(-30, -23);
pcanvas_->translate(-30, -23);
vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate2")));
}
vcanvas_->resetMatrix();
pcanvas_->resetMatrix();
// For scaling and rotation, they use a different algorithm (nearest
// neighborhood vs smoothing). At least verify that the output doesn't change
// across versions.
compare_canvas_ = false;
{
vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("scale")));
}
vcanvas_->resetMatrix();
pcanvas_->resetMatrix();
{
vcanvas_->rotate(67);
pcanvas_->rotate(67);
vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rotate")));
}
}
#endif // !defined(USE_AURA)
} // namespace skia
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "skia/ext/vector_platform_device_emf_win.h"
#include <windows.h>
#include "base/logging.h"
#include "base/strings/string16.h"
#include "skia/ext/bitmap_platform_device.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkPathEffect.h"
#include "third_party/skia/include/core/SkTemplates.h"
#include "third_party/skia/include/core/SkUtils.h"
#include "third_party/skia/include/ports/SkTypeface_win.h"
namespace skia {
#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
// static
SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice(
int width, int height, bool is_opaque, HANDLE shared_section) {
if (!is_opaque) {
// TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent
// layer, i.e. merging it, we need to rasterize it because GDI doesn't
// support transparency except for AlphaBlend(). Right now, a
// BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
// call is being done. The way to save a layer would be to create an
// EMF-based VectorDevice and have this device registers the drawing. When
// playing back the device into a bitmap, do it at the printer's dpi instead
// of the layout's dpi (which is much lower).
return BitmapPlatformDevice::Create(width, height, is_opaque,
shared_section);
}
// TODO(maruel): http://crbug.com/18383 Look if it would be worth to
// increase the resolution by ~10x (any worthy factor) to increase the
// rendering precision (think about printing) while using a relatively
// low dpi. This happens because we receive float as input but the GDI
// functions works with integers. The idea is to premultiply the matrix
// with this factor and multiply each SkScalar that are passed to
// SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
// doing the same for text rendering.
SkASSERT(shared_section);
SkBaseDevice* device = VectorPlatformDeviceEmf::create(
reinterpret_cast<HDC>(shared_section), width, height);
return device;
}
static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
hdr->biSize = sizeof(BITMAPINFOHEADER);
hdr->biWidth = width;
hdr->biHeight = -height; // Minus means top-down bitmap.
hdr->biPlanes = 1;
hdr->biBitCount = 32;
hdr->biCompression = BI_RGB; // no compression
hdr->biSizeImage = 0;
hdr->biXPelsPerMeter = 1;
hdr->biYPelsPerMeter = 1;
hdr->biClrUsed = 0;
hdr->biClrImportant = 0;
}
SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
InitializeDC(dc);
// Link the SkBitmap to the current selected bitmap in the device context.
SkBitmap bitmap;
HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
bool succeeded = false;
if (selected_bitmap != NULL) {
BITMAP bitmap_data = {0};
if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
sizeof(BITMAP)) {
// The context has a bitmap attached. Attach our SkBitmap to it.
// Warning: If the bitmap gets unselected from the HDC,
// VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
// could be released while SkBitmap still has a reference to it. Be
// cautious.
if (width == bitmap_data.bmWidth && height == bitmap_data.bmHeight) {
SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
succeeded = bitmap.installPixels(info, bitmap_data.bmBits,
bitmap_data.bmWidthBytes);
}
}
}
if (!succeeded)
bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
return new VectorPlatformDeviceEmf(dc, bitmap);
}
VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
: SkBitmapDevice(bitmap),
hdc_(dc),
previous_brush_(NULL),
previous_pen_(NULL) {
transform_.reset();
SetPlatformDevice(this, this);
}
VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
SkASSERT(previous_brush_ == NULL);
SkASSERT(previous_pen_ == NULL);
}
HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
return hdc_;
}
void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
const SkPaint& paint) {
// TODO(maruel): Bypass the current transformation matrix.
SkRect rect;
rect.fLeft = 0;
rect.fTop = 0;
rect.fRight = SkIntToScalar(width() + 1);
rect.fBottom = SkIntToScalar(height() + 1);
drawRect(draw, rect, paint);
}
void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
SkCanvas::PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) {
if (!count)
return;
if (mode == SkCanvas::kPoints_PointMode) {
SkASSERT(false);
return;
}
SkPaint tmp_paint(paint);
tmp_paint.setStyle(SkPaint::kStroke_Style);
// Draw a path instead.
SkPath path;
switch (mode) {
case SkCanvas::kLines_PointMode:
if (count % 2) {
SkASSERT(false);
return;
}
for (size_t i = 0; i < count / 2; ++i) {
path.moveTo(pts[2 * i]);
path.lineTo(pts[2 * i + 1]);
}
break;
case SkCanvas::kPolygon_PointMode:
path.moveTo(pts[0]);
for (size_t i = 1; i < count; ++i) {
path.lineTo(pts[i]);
}
break;
default:
SkASSERT(false);
return;
}
// Draw the calculated path.
drawPath(draw, path, tmp_paint);
}
void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
const SkRect& rect,
const SkPaint& paint) {
CHECK_FOR_NODRAW_ANNOTATION(paint);
if (paint.getPathEffect()) {
// Draw a path instead.
SkPath path_orginal;
path_orginal.addRect(rect);
// Apply the path effect to the rect.
SkPath path_modified;
paint.getFillPath(path_orginal, &path_modified);
// Removes the path effect from the temporary SkPaint object.
SkPaint paint_no_effet(paint);
paint_no_effet.setPathEffect(NULL);
// Draw the calculated path.
drawPath(draw, path_modified, paint_no_effet);
return;
}
if (!ApplyPaint(paint)) {
return;
}
HDC dc = BeginPlatformPaint();
if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft),
SkScalarRoundToInt(rect.fTop),
SkScalarRoundToInt(rect.fRight),
SkScalarRoundToInt(rect.fBottom))) {
SkASSERT(false);
}
EndPlatformPaint();
Cleanup();
}
void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
const SkPaint& paint) {
SkPath path;
path.addRRect(rr);
this->drawPath(draw, path, paint, NULL, true);
}
void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
const SkPath& path,
const SkPaint& paint,
const SkMatrix* prePathMatrix,
bool pathIsMutable) {
CHECK_FOR_NODRAW_ANNOTATION(paint);
if (paint.getPathEffect()) {
// Apply the path effect forehand.
SkPath path_modified;
paint.getFillPath(path, &path_modified);
// Removes the path effect from the temporary SkPaint object.
SkPaint paint_no_effet(paint);
paint_no_effet.setPathEffect(NULL);
// Draw the calculated path.
drawPath(draw, path_modified, paint_no_effet);
return;
}
if (!ApplyPaint(paint)) {
return;
}
HDC dc = BeginPlatformPaint();
if (PlatformDevice::LoadPathToDC(dc, path)) {
switch (paint.getStyle()) {
case SkPaint::kFill_Style: {
BOOL res = StrokeAndFillPath(dc);
SkASSERT(res != 0);
break;
}
case SkPaint::kStroke_Style: {
BOOL res = StrokePath(dc);
SkASSERT(res != 0);
break;
}
case SkPaint::kStrokeAndFill_Style: {
BOOL res = StrokeAndFillPath(dc);
SkASSERT(res != 0);
break;
}
default:
SkASSERT(false);
break;
}
}
EndPlatformPaint();
Cleanup();
}
void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
const SkBitmap& bitmap,
const SkRect* src,
const SkRect& dst,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags) {
SkMatrix matrix;
SkRect bitmapBounds, tmpSrc, tmpDst;
SkBitmap tmpBitmap;
bitmapBounds.isetWH(bitmap.width(), bitmap.height());
// Compute matrix from the two rectangles
if (src) {
tmpSrc = *src;
} else {
tmpSrc = bitmapBounds;
}
matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
const SkBitmap* bitmapPtr = &bitmap;
// clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
// needed (if the src was clipped). No check needed if src==null.
if (src) {
if (!bitmapBounds.contains(*src)) {
if (!tmpSrc.intersect(bitmapBounds)) {
return; // nothing to draw
}
// recompute dst, based on the smaller tmpSrc
matrix.mapRect(&tmpDst, tmpSrc);
}
// since we may need to clamp to the borders of the src rect within
// the bitmap, we extract a subset.
// TODO: make sure this is handled in drawrect and remove it from here.
SkIRect srcIR;
tmpSrc.roundOut(&srcIR);
if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
return;
}
bitmapPtr = &tmpBitmap;
// Since we did an extract, we need to adjust the matrix accordingly
SkScalar dx = 0, dy = 0;
if (srcIR.fLeft > 0) {
dx = SkIntToScalar(srcIR.fLeft);
}
if (srcIR.fTop > 0) {
dy = SkIntToScalar(srcIR.fTop);
}
if (dx || dy) {
matrix.preTranslate(dx, dy);
}
}
this->drawBitmap(draw, *bitmapPtr, matrix, paint);
}
void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
const SkBitmap& bitmap,
const SkMatrix& matrix,
const SkPaint& paint) {
// Load the temporary matrix. This is what will translate, rotate and resize
// the bitmap.
SkMatrix actual_transform(transform_);
actual_transform.preConcat(matrix);
LoadTransformToDC(hdc_, actual_transform);
InternalDrawBitmap(bitmap, 0, 0, paint);
// Restore the original matrix.
LoadTransformToDC(hdc_, transform_);
}
void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
const SkBitmap& bitmap,
int x, int y,
const SkPaint& paint) {
SkMatrix identity;
identity.reset();
LoadTransformToDC(hdc_, identity);
InternalDrawBitmap(bitmap, x, y, paint);
// Restore the original matrix.
LoadTransformToDC(hdc_, transform_);
}
/////////////////////////////////////////////////////////////////////////
static bool gdiCanHandleText(const SkPaint& paint) {
return !paint.getShader() &&
!paint.getPathEffect() &&
(SkPaint::kFill_Style == paint.getStyle()) &&
(255 == paint.getAlpha());
}
class SkGDIFontSetup {
public:
SkGDIFontSetup() :
fHDC(NULL),
fNewFont(NULL),
fSavedFont(NULL),
fSavedTextColor(0),
fUseGDI(false) {
SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
}
~SkGDIFontSetup();
// can only be called once
bool useGDI(HDC hdc, const SkPaint&);
private:
HDC fHDC;
HFONT fNewFont;
HFONT fSavedFont;
COLORREF fSavedTextColor;
bool fUseGDI;
SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
};
bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
SkASSERT(!fUseGDIHasBeenCalled);
SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
fUseGDI = gdiCanHandleText(paint);
if (fUseGDI) {
fSavedTextColor = GetTextColor(hdc);
SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
LOGFONT lf = {0};
SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize());
fNewFont = CreateFontIndirect(&lf);
fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
fHDC = hdc;
}
return fUseGDI;
}
SkGDIFontSetup::~SkGDIFontSetup() {
if (fUseGDI) {
::SelectObject(fHDC, fSavedFont);
::DeleteObject(fNewFont);
SetTextColor(fHDC, fSavedTextColor);
}
}
static SkScalar getAscent(const SkPaint& paint) {
SkPaint::FontMetrics fm;
paint.getFontMetrics(&fm);
return fm.fAscent;
}
// return the options int for ExtTextOut. Only valid if the paint's text
// encoding is not UTF8 (in which case ExtTextOut can't be used).
static UINT getTextOutOptions(const SkPaint& paint) {
if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
return ETO_GLYPH_INDEX;
} else {
SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
return 0;
}
}
static SkiaEnsureTypefaceCharactersAccessible
g_skia_ensure_typeface_characters_accessible = NULL;
SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
SkiaEnsureTypefaceCharactersAccessible func) {
// This function is supposed to be called once in process life time.
SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
g_skia_ensure_typeface_characters_accessible = func;
}
void EnsureTypefaceCharactersAccessible(
const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
LOGFONT lf = {0};
SkLOGFONTFromTypeface(&typeface, &lf);
g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
}
bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
LPCWSTR text, unsigned int characters, const int * lpDx,
SkTypeface* const typeface) {
bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
if (!success) {
if (typeface) {
EnsureTypefaceCharactersAccessible(*typeface,
text,
characters);
success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
if (!success) {
LOGFONT lf = {0};
SkLOGFONTFromTypeface(typeface, &lf);
VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
<< " FaceName = " << lf.lfFaceName
<< " and characters: " << base::string16(text, characters);
}
} else {
VLOG(1) << "ExtTextOut FAILED for default FaceName "
<< " and characters: " << base::string16(text, characters);
}
}
return success;
}
void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
const void* text,
size_t byteLength,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
SkGDIFontSetup setup;
bool useDrawPath = true;
if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
&& setup.useGDI(hdc_, paint)) {
UINT options = getTextOutOptions(paint);
UINT count = byteLength >> 1;
useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRoundToInt(x),
SkScalarRoundToInt(y + getAscent(paint)), options, 0,
reinterpret_cast<const wchar_t*>(text), count, NULL,
paint.getTypeface());
}
if (useDrawPath) {
SkPath path;
paint.getTextPath(text, byteLength, x, y, &path);
drawPath(draw, path, paint);
}
}
static size_t size_utf8(const char* text) {
return SkUTF8_CountUTF8Bytes(text);
}
static size_t size_utf16(const char* text) {
uint16_t c = *reinterpret_cast<const uint16_t*>(text);
return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
}
static size_t size_glyphid(const char* text) {
return 2;
}
void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
const void* text,
size_t len,
const SkScalar pos[],
int scalarsPerPos,
const SkPoint& offset,
const SkPaint& paint) {
SkGDIFontSetup setup;
bool useDrawText = true;
if (scalarsPerPos == 2 && len >= 2 &&
SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() &&
setup.useGDI(hdc_, paint)) {
int startX = SkScalarRoundToInt(pos[0] + offset.x());
int startY = SkScalarRoundToInt(pos[1] + offset.y() + getAscent(paint));
const int count = len >> 1;
SkAutoSTMalloc<64, INT> storage(count);
INT* advances = storage.get();
for (int i = 0; i < count - 1; ++i) {
advances[i] = SkScalarRoundToInt(pos[2] - pos[0]);
pos += 2;
}
advances[count - 1] = 0;
useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
count, advances, paint.getTypeface());
}
if (useDrawText) {
size_t (*bytesPerCodePoint)(const char*);
switch (paint.getTextEncoding()) {
case SkPaint::kUTF8_TextEncoding:
bytesPerCodePoint = size_utf8;
break;
case SkPaint::kUTF16_TextEncoding:
bytesPerCodePoint = size_utf16;
break;
default:
SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
bytesPerCodePoint = size_glyphid;
break;
}
const char* curr = reinterpret_cast<const char*>(text);
const char* stop = curr + len;
while (curr < stop) {
SkScalar x = offset.x() + pos[0];
SkScalar y = offset.y() + (2 == scalarsPerPos ? pos[1] : 0);
size_t bytes = bytesPerCodePoint(curr);
drawText(draw, curr, bytes, x, y, paint);
curr += bytes;
pos += scalarsPerPos;
}
}
}
void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
const void* text,
size_t len,
const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint) {
// This function isn't used in the code. Verify this assumption.
SkASSERT(false);
}
void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
SkCanvas::VertexMode vmode,
int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[],
SkXfermode* xmode,
const uint16_t indices[],
int indexCount,
const SkPaint& paint) {
// This function isn't used in the code. Verify this assumption.
SkASSERT(false);
}
void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
SkBaseDevice* device,
int x,
int y,
const SkPaint& paint) {
// TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
// it is a vectorial device.
drawSprite(draw, device->accessBitmap(false), x, y, paint);
}
bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
// Note: The goal here is to transfert the SkPaint's state to the HDC's state.
// This function does not execute the SkPaint drawing commands. These should
// be executed in drawPaint().
SkPaint::Style style = paint.getStyle();
if (!paint.getAlpha())
style = (SkPaint::Style) SkPaint::kStyleCount;
switch (style) {
case SkPaint::kFill_Style:
if (!CreateBrush(true, paint) ||
!CreatePen(false, paint))
return false;
break;
case SkPaint::kStroke_Style:
if (!CreateBrush(false, paint) ||
!CreatePen(true, paint))
return false;
break;
case SkPaint::kStrokeAndFill_Style:
if (!CreateBrush(true, paint) ||
!CreatePen(true, paint))
return false;
break;
default:
if (!CreateBrush(false, paint) ||
!CreatePen(false, paint))
return false;
break;
}
/*
getFlags();
isAntiAlias();
isDither()
isLinearText()
isSubpixelText()
isUnderlineText()
isStrikeThruText()
isFakeBoldText()
isDevKernText()
isFilterBitmap()
// Skia's text is not used. This should be fixed.
getTextAlign()
getTextScaleX()
getTextSkewX()
getTextEncoding()
getFontMetrics()
getFontSpacing()
*/
// BUG 1094907: Implement shaders. Shaders currently in use:
// SkShader::CreateBitmapShader
// SkGradientShader::CreateRadial
// SkGradientShader::CreateLinear
// SkASSERT(!paint.getShader());
// http://b/1106647 Implement loopers and mask filter. Looper currently in
// use:
// SkBlurDrawLooper is used for shadows.
// SkASSERT(!paint.getLooper());
// SkASSERT(!paint.getMaskFilter());
// http://b/1165900 Implement xfermode.
// SkASSERT(!paint.getXfermode());
// The path effect should be processed before arriving here.
SkASSERT(!paint.getPathEffect());
// This isn't used in the code. Verify this assumption.
SkASSERT(!paint.getRasterizer());
// Reuse code to load Win32 Fonts.
return true;
}
void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
const SkRegion& region,
const SkClipStack&) {
transform_ = transform;
LoadTransformToDC(hdc_, transform_);
clip_region_ = region;
if (!clip_region_.isEmpty())
LoadClipRegion();
}
void VectorPlatformDeviceEmf::LoadClipRegion() {
SkMatrix t;
t.reset();
LoadClippingRegionToDC(hdc_, clip_region_, t);
}
SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
const CreateInfo& info) {
SkASSERT(info.fInfo.colorType() == kN32_SkColorType);
return VectorPlatformDeviceEmf::CreateDevice(
info.fInfo.width(), info.fInfo.height(), info.fInfo.isOpaque(), NULL);
}
bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
SkASSERT(previous_brush_ == NULL);
// We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
// SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
// WHITE_BRUSH instead.
if (!use_brush) {
// Set the transparency.
if (0 == SetBkMode(hdc_, TRANSPARENT)) {
SkASSERT(false);
return false;
}
// Select the NULL brush.
previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
return previous_brush_ != NULL;
}
// Set the opacity.
if (0 == SetBkMode(hdc_, OPAQUE)) {
SkASSERT(false);
return false;
}
// Create and select the brush.
previous_brush_ = SelectObject(CreateSolidBrush(color));
return previous_brush_ != NULL;
}
bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
COLORREF color,
int stroke_width,
float stroke_miter,
DWORD pen_style) {
SkASSERT(previous_pen_ == NULL);
// We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
// SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
// instead.
// No pen case
if (!use_pen) {
previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
return previous_pen_ != NULL;
}
// Use the stock pen if the stroke width is 0.
if (stroke_width == 0) {
// Create a pen with the right color.
previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
return previous_pen_ != NULL;
}
// Load a custom pen.
LOGBRUSH brush = {0};
brush.lbStyle = BS_SOLID;
brush.lbColor = color;
brush.lbHatch = 0;
HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
SkASSERT(pen != NULL);
previous_pen_ = SelectObject(pen);
if (previous_pen_ == NULL)
return false;
if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
SkASSERT(false);
return false;
}
return true;
}
void VectorPlatformDeviceEmf::Cleanup() {
if (previous_brush_) {
HGDIOBJ result = SelectObject(previous_brush_);
previous_brush_ = NULL;
if (result) {
BOOL res = DeleteObject(result);
SkASSERT(res != 0);
}
}
if (previous_pen_) {
HGDIOBJ result = SelectObject(previous_pen_);
previous_pen_ = NULL;
if (result) {
BOOL res = DeleteObject(result);
SkASSERT(res != 0);
}
}
// Remove any loaded path from the context.
AbortPath(hdc_);
}
HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
HGDIOBJ result = ::SelectObject(hdc_, object);
SkASSERT(result != HGDI_ERROR);
if (result == HGDI_ERROR)
return NULL;
return result;
}
bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
const SkPaint& paint) {
// Make sure that for transparent color, no brush is used.
if (paint.getAlpha() == 0) {
use_brush = false;
}
return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
}
bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
// Make sure that for transparent color, no pen is used.
if (paint.getAlpha() == 0) {
use_pen = false;
}
DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
switch (paint.getStrokeJoin()) {
case SkPaint::kMiter_Join:
// Connects path segments with a sharp join.
pen_style |= PS_JOIN_MITER;
break;
case SkPaint::kRound_Join:
// Connects path segments with a round join.
pen_style |= PS_JOIN_ROUND;
break;
case SkPaint::kBevel_Join:
// Connects path segments with a flat bevel join.
pen_style |= PS_JOIN_BEVEL;
break;
default:
SkASSERT(false);
break;
}
switch (paint.getStrokeCap()) {
case SkPaint::kButt_Cap:
// Begin/end contours with no extension.
pen_style |= PS_ENDCAP_FLAT;
break;
case SkPaint::kRound_Cap:
// Begin/end contours with a semi-circle extension.
pen_style |= PS_ENDCAP_ROUND;
break;
case SkPaint::kSquare_Cap:
// Begin/end contours with a half square extension.
pen_style |= PS_ENDCAP_SQUARE;
break;
default:
SkASSERT(false);
break;
}
return CreatePen(use_pen,
SkColorToCOLORREF(paint.getColor()),
SkScalarRoundToInt(paint.getStrokeWidth()),
paint.getStrokeMiter(),
pen_style);
}
void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
int x, int y,
const SkPaint& paint) {
unsigned char alpha = paint.getAlpha();
if (alpha == 0)
return;
bool is_translucent;
if (alpha != 255) {
// ApplyPaint expect an opaque color.
SkPaint tmp_paint(paint);
tmp_paint.setAlpha(255);
if (!ApplyPaint(tmp_paint))
return;
is_translucent = true;
} else {
if (!ApplyPaint(paint))
return;
is_translucent = false;
}
int src_size_x = bitmap.width();
int src_size_y = bitmap.height();
if (!src_size_x || !src_size_y)
return;
// Create a BMP v4 header that we can serialize. We use the shared "V3"
// fillter to fill the stardard items, then add in the "V4" stuff we want.
BITMAPV4HEADER bitmap_header = {0};
FillBitmapInfoHeader(src_size_x, src_size_y,
reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
bitmap_header.bV4RedMask = 0x00ff0000;
bitmap_header.bV4GreenMask = 0x0000ff00;
bitmap_header.bV4BlueMask = 0x000000ff;
bitmap_header.bV4AlphaMask = 0xff000000;
SkAutoLockPixels lock(bitmap);
SkASSERT(bitmap.colorType() == kN32_SkColorType);
const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
if (pixels == NULL) {
SkASSERT(false);
return;
}
if (!is_translucent) {
int row_length = bitmap.rowBytesAsPixels();
// There is no quick way to determine if an image is opaque.
for (int y2 = 0; y2 < src_size_y; ++y2) {
for (int x2 = 0; x2 < src_size_x; ++x2) {
if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
is_translucent = true;
y2 = src_size_y;
break;
}
}
}
}
HDC dc = BeginPlatformPaint();
BITMAPINFOHEADER hdr = {0};
FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
if (is_translucent) {
// The image must be loaded as a bitmap inside a device context.
HDC bitmap_dc = ::CreateCompatibleDC(dc);
void* bits = NULL;
HBITMAP hbitmap = ::CreateDIBSection(
bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
DIB_RGB_COLORS, &bits, NULL, 0);
// static cast to a char so we can do byte ptr arithmatic to
// get the offset.
unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
// We will copy row by row to avoid having to worry about
// the row strides being different.
const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
for (int row = 0; row < bitmap.height(); ++row) {
int dest_offset = row * dest_row_size;
// pixels_offset in terms of pixel count.
int src_offset = row * bitmap.rowBytesAsPixels();
memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
}
SkASSERT(hbitmap);
HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
// After some analysis of IE7's behavior, this is the thing to do. I was
// sure IE7 was doing so kind of bitmasking due to the way translucent image
// where renderered but after some windbg tracing, it is being done by the
// printer driver after all (mostly HP printers). IE7 always use AlphaBlend
// for bitmasked images. The trick seems to switch the stretching mode in
// what the driver expects.
DWORD previous_mode = GetStretchBltMode(dc);
BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
SkASSERT(result);
// Note that this function expect premultiplied colors (!)
BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
result = GdiAlphaBlend(dc,
x, y, // Destination origin.
src_size_x, src_size_y, // Destination size.
bitmap_dc,
0, 0, // Source origin.
src_size_x, src_size_y, // Source size.
blend_function);
SkASSERT(result);
result = SetStretchBltMode(dc, previous_mode);
SkASSERT(result);
::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
DeleteObject(hbitmap);
DeleteDC(bitmap_dc);
} else {
int nCopied = StretchDIBits(dc,
x, y, // Destination origin.
src_size_x, src_size_y,
0, 0, // Source origin.
src_size_x, src_size_y, // Source size.
pixels,
reinterpret_cast<const BITMAPINFO*>(&hdr),
DIB_RGB_COLORS,
SRCCOPY);
}
EndPlatformPaint();
Cleanup();
}
} // namespace skia
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "skia/ext/platform_device.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkRegion.h"
namespace skia {
// A device is basically a wrapper around SkBitmap that provides a surface for
// SkCanvas to draw into. This specific device is not not backed by a surface
// and is thus unreadable. This is because the backend is completely vectorial.
// This device is a simple wrapper over a Windows device context (HDC) handle.
// TODO(robertphillips): Once Skia's SkBaseDevice is refactored to remove
// the bitmap-specific entry points, this class should derive from it.
class VectorPlatformDeviceEmf : public SkBitmapDevice, public PlatformDevice {
public:
SK_API static SkBaseDevice* CreateDevice(int width, int height, bool isOpaque,
HANDLE shared_section);
// Factory function. The DC is kept as the output context.
static SkBaseDevice* create(HDC dc, int width, int height);
VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap);
virtual ~VectorPlatformDeviceEmf();
// PlatformDevice methods
virtual PlatformSurface BeginPlatformPaint() override;
// SkBaseDevice methods.
virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) override;
virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
size_t count, const SkPoint[],
const SkPaint& paint) override;
virtual void drawRect(const SkDraw& draw, const SkRect& r,
const SkPaint& paint) override;
virtual void drawRRect(const SkDraw&, const SkRRect& rr,
const SkPaint& paint) override;
virtual void drawPath(const SkDraw& draw, const SkPath& path,
const SkPaint& paint,
const SkMatrix* prePathMatrix = NULL,
bool pathIsMutable = false) override;
virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
const SkRect* src, const SkRect& dst,
const SkPaint& paint,
SkCanvas::DrawBitmapRectFlags flags) override;
virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
const SkMatrix& matrix,
const SkPaint& paint) override;
virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint) override;
virtual void drawText(const SkDraw& draw, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint) override;
virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
const SkScalar pos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint& paint) override;
virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) override;
virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode,
int vertexCount,
const SkPoint verts[], const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) override;
virtual void drawDevice(const SkDraw& draw, SkBaseDevice*, int x, int y,
const SkPaint&) override;
virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
const SkClipStack&) override;
void LoadClipRegion();
protected:
virtual SkBaseDevice* onCreateCompatibleDevice(const CreateInfo& info) override;
private:
// Applies the SkPaint's painting properties in the current GDI context, if
// possible. If GDI can't support all paint's properties, returns false. It
// doesn't execute the "commands" in SkPaint.
bool ApplyPaint(const SkPaint& paint);
// Selects a new object in the device context. It can be a pen, a brush, a
// clipping region, a bitmap or a font. Returns the old selected object.
HGDIOBJ SelectObject(HGDIOBJ object);
// Creates a brush according to SkPaint's properties.
bool CreateBrush(bool use_brush, const SkPaint& paint);
// Creates a pen according to SkPaint's properties.
bool CreatePen(bool use_pen, const SkPaint& paint);
// Restores back the previous objects (pen, brush, etc) after a paint command.
void Cleanup();
// Creates a brush according to SkPaint's properties.
bool CreateBrush(bool use_brush, COLORREF color);
// Creates a pen according to SkPaint's properties.
bool CreatePen(bool use_pen, COLORREF color, int stroke_width,
float stroke_miter, DWORD pen_style);
// Draws a bitmap in the the device, using the currently loaded matrix.
void InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
const SkPaint& paint);
// The Windows Device Context handle. It is the backend used with GDI drawing.
// This backend is write-only and vectorial.
HDC hdc_;
// Translation assigned to the DC: we need to keep track of this separately
// so it can be updated even if the DC isn't created yet.
SkMatrix transform_;
// The current clipping
SkRegion clip_region_;
// Previously selected brush before the current drawing.
HGDIOBJ previous_brush_;
// Previously selected pen before the current drawing.
HGDIOBJ previous_pen_;
DISALLOW_COPY_AND_ASSIGN(VectorPlatformDeviceEmf);
};
typedef void (*SkiaEnsureTypefaceCharactersAccessible)
(const LOGFONT& font, const wchar_t* text, unsigned int text_length);
SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
SkiaEnsureTypefaceCharactersAccessible func);
} // namespace skia
#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
...@@ -77,9 +77,6 @@ ...@@ -77,9 +77,6 @@
'ext/skia_utils_mac.h', 'ext/skia_utils_mac.h',
'ext/skia_utils_win.cc', 'ext/skia_utils_win.cc',
'ext/skia_utils_win.h', 'ext/skia_utils_win.h',
'ext/vector_canvas.h',
'ext/vector_platform_device_emf_win.cc',
'ext/vector_platform_device_emf_win.h',
], ],
'conditions': [ 'conditions': [
[ 'OS == "android" and ' [ 'OS == "android" and '
......
...@@ -29,14 +29,8 @@ ...@@ -29,14 +29,8 @@
'ext/refptr_unittest.cc', 'ext/refptr_unittest.cc',
'ext/skia_utils_ios_unittest.mm', 'ext/skia_utils_ios_unittest.mm',
'ext/skia_utils_mac_unittest.mm', 'ext/skia_utils_mac_unittest.mm',
'ext/vector_canvas_unittest.cc',
], ],
'conditions': [ 'conditions': [
['OS != "win"', {
'sources!': [
'ext/vector_canvas_unittest.cc',
],
}],
['OS != "win" and OS != "mac"', { ['OS != "win" and OS != "mac"', {
'sources!': [ 'sources!': [
'ext/platform_canvas_unittest.cc', 'ext/platform_canvas_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment