summaryrefslogtreecommitdiffstats
path: root/skia/ext/platform_canvas_win.cc
blob: fe0f8524a9d58f7fb456f3fde0d19ad9bec34e6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2006-2008 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 <windows.h>
#include <psapi.h>

#include "skia/ext/platform_canvas_win.h"

#include "skia/ext/bitmap_platform_device_win.h"

namespace skia {

// Crash on failure.
#define CHECK(condition) if (!(condition)) __debugbreak();

// Crashes the process. This is called when a bitmap allocation fails, and this
// function tries to determine why it might have failed, and crash on different
// lines. This allows us to see in crash dumps the most likely reason for the
// failure. It takes the size of the bitmap we were trying to allocate as its
// arguments so we can check that as well.
//
// Note that in a sandboxed renderer this function crashes when trying to
// call GetProcessMemoryInfo() because it tries to load psapi.dll, which
// is fine but gives you a very hard to read crash dump.
__declspec(noinline) void CrashForBitmapAllocationFailure(int w, int h) {
  // If the bitmap is ginormous, then we probably can't allocate it.
  // We use 64M pixels = 256MB @ 4 bytes per pixel.
  const __int64 kGinormousBitmapPxl = 64000000;
  CHECK(static_cast<__int64>(w) * static_cast<__int64>(h) <
        kGinormousBitmapPxl);

  // The maximum number of GDI objects per process is 10K. If we're very close
  // to that, it's probably the problem.
  const int kLotsOfGDIObjs = 9990;
  CHECK(GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS) < kLotsOfGDIObjs);

  // If we're using a crazy amount of virtual address space, then maybe there
  // isn't enough for our bitmap.
  const __int64 kLotsOfMem = 1500000000;  // 1.5GB.
  PROCESS_MEMORY_COUNTERS pmc;
  if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
    CHECK(pmc.PagefileUsage < kLotsOfMem);

  // Everything else.
  CHECK(0);
}

// Crashes the process. This is called when a bitmap allocation fails but
// unlike its cousin CrashForBitmapAllocationFailure() it tries to detect if
// the issue was a non-valid shared bitmap handle. 
__declspec(noinline) void CrashIfInvalidSection(HANDLE shared_section) {
  DWORD handle_info = 0;
  CHECK(::GetHandleInformation(shared_section, &handle_info) == TRUE);
}

PlatformCanvasWin::PlatformCanvasWin() : SkCanvas() {
}

PlatformCanvasWin::PlatformCanvasWin(int width, int height, bool is_opaque)
    : SkCanvas() {
  bool initialized = initialize(width, height, is_opaque, NULL);
  if (!initialized)
    CrashForBitmapAllocationFailure(width, height);
}

PlatformCanvasWin::PlatformCanvasWin(int width,
                                     int height,
                                     bool is_opaque,
                                     HANDLE shared_section)
    : SkCanvas() {
  bool initialized = initialize(width, height, is_opaque, shared_section);
  if (!initialized) {
    CrashIfInvalidSection(shared_section);
    CrashForBitmapAllocationFailure(width, height);
  }
}

PlatformCanvasWin::~PlatformCanvasWin() {
}

bool PlatformCanvasWin::initialize(int width,
                                   int height,
                                   bool is_opaque,
                                   HANDLE shared_section) {
  SkDevice* device =
      createPlatformDevice(width, height, is_opaque, shared_section);
  if (!device)
    return false;

  setDevice(device);
  device->unref();  // was created with refcount 1, and setDevice also refs
  return true;
}

HDC PlatformCanvasWin::beginPlatformPaint() {
  return getTopPlatformDevice().getBitmapDC();
}

void PlatformCanvasWin::endPlatformPaint() {
  // we don't clear the DC here since it will be likely to be used again
  // flushing will be done in onAccessBitmap
}

PlatformDeviceWin& PlatformCanvasWin::getTopPlatformDevice() const {
  // All of our devices should be our special PlatformDevice.
  SkCanvas::LayerIter iter(const_cast<PlatformCanvasWin*>(this), false);
  return *static_cast<PlatformDeviceWin*>(iter.device());
}

SkDevice* PlatformCanvasWin::createDevice(SkBitmap::Config config,
                                          int width,
                                          int height,
                                          bool is_opaque, bool isForLayer) {
  SkASSERT(config == SkBitmap::kARGB_8888_Config);
  return createPlatformDevice(width, height, is_opaque, NULL);
}

SkDevice* PlatformCanvasWin::createPlatformDevice(int width,
                                                  int height,
                                                  bool is_opaque,
                                                  HANDLE shared_section) {
  HDC screen_dc = GetDC(NULL);
  SkDevice* device = BitmapPlatformDeviceWin::create(screen_dc, width, height,
                                                  is_opaque, shared_section);
  ReleaseDC(NULL, screen_dc);
  return device;
}

SkDevice* PlatformCanvasWin::setBitmapDevice(const SkBitmap&) {
  SkASSERT(false);  // Should not be called.
  return NULL;
}

// static
size_t PlatformCanvasWin::StrideForWidth(unsigned width) {
  return 4 * width;
}

}  // namespace skia