Home | History | Annotate | Download | only in ext
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <windows.h>
      6 #include <psapi.h>
      7 
      8 #include "base/logging.h"
      9 #include "base/debug/alias.h"
     10 #include "skia/ext/bitmap_platform_device_win.h"
     11 #include "skia/ext/platform_canvas.h"
     12 #include "third_party/skia/include/core/SkMatrix.h"
     13 #include "third_party/skia/include/core/SkRefCnt.h"
     14 #include "third_party/skia/include/core/SkRegion.h"
     15 #include "third_party/skia/include/core/SkUtils.h"
     16 
     17 namespace {
     18 
     19 // PlatformBitmapPixelRef is an SkPixelRef that, on Windows, is backed by an
     20 // HBITMAP.
     21 class SK_API PlatformBitmapPixelRef : public SkPixelRef {
     22  public:
     23   PlatformBitmapPixelRef(HBITMAP bitmap_handle, void* pixels);
     24   virtual ~PlatformBitmapPixelRef();
     25 
     26   SK_DECLARE_UNFLATTENABLE_OBJECT();
     27 
     28  protected:
     29   virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
     30   virtual void onUnlockPixels() SK_OVERRIDE;
     31 
     32  private:
     33   HBITMAP bitmap_handle_;
     34   void* pixels_;
     35 };
     36 
     37 HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
     38                              HANDLE shared_section, void** data) {
     39   // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
     40   // just create a minimal bitmap
     41   if ((width == 0) || (height == 0)) {
     42     width = 1;
     43     height = 1;
     44   }
     45 
     46   BITMAPINFOHEADER hdr = {0};
     47   hdr.biSize = sizeof(BITMAPINFOHEADER);
     48   hdr.biWidth = width;
     49   hdr.biHeight = -height;  // minus means top-down bitmap
     50   hdr.biPlanes = 1;
     51   hdr.biBitCount = 32;
     52   hdr.biCompression = BI_RGB;  // no compression
     53   hdr.biSizeImage = 0;
     54   hdr.biXPelsPerMeter = 1;
     55   hdr.biYPelsPerMeter = 1;
     56   hdr.biClrUsed = 0;
     57   hdr.biClrImportant = 0;
     58 
     59   HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
     60                                      0, data, shared_section, 0);
     61 
     62   // If this call fails, we're gonna crash hard. Try to get some useful
     63   // information out before we crash for post-mortem analysis.
     64   if (!hbitmap) {
     65     // Make sure parameters are saved in the minidump.
     66     base::debug::Alias(&width);
     67     base::debug::Alias(&height);
     68 
     69     int last_error = GetLastError();
     70     base::debug::Alias(&last_error);
     71 
     72     int num_gdi_handles = GetGuiResources(GetCurrentProcess(),
     73                                           GR_GDIOBJECTS);
     74     if (num_gdi_handles == 0) {
     75       int get_gui_resources_error = GetLastError();
     76       base::debug::Alias(&get_gui_resources_error);
     77       CHECK(false);
     78     }
     79 
     80     base::debug::Alias(&num_gdi_handles);
     81     const int kLotsOfHandles = 9990;
     82     if (num_gdi_handles > kLotsOfHandles)
     83       CHECK(false);
     84 
     85     PROCESS_MEMORY_COUNTERS_EX pmc;
     86     pmc.cb = sizeof(pmc);
     87     if (!GetProcessMemoryInfo(GetCurrentProcess(),
     88                               reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
     89                               sizeof(pmc))) {
     90       CHECK(false);
     91     }
     92     const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
     93     if (pmc.PagefileUsage > kLotsOfMemory)
     94       CHECK(false);
     95     if (pmc.PrivateUsage > kLotsOfMemory)
     96       CHECK(false);
     97 
     98     // Huh, that's weird.  We don't have crazy handle count, we don't have
     99     // ridiculous memory usage.  Try to allocate a small bitmap and see if that
    100     // fails too.
    101     hdr.biWidth = 5;
    102     hdr.biHeight = 5;
    103     void* small_data;
    104     HBITMAP small_bitmap = CreateDIBSection(
    105         NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
    106         0, &small_data, shared_section, 0);
    107     if (!small_bitmap)
    108       CHECK(false);
    109     BITMAP bitmap_data;
    110     if (GetObject(small_bitmap, sizeof(BITMAP), &bitmap_data)) {
    111       if (!DeleteObject(small_bitmap))
    112         CHECK(false);
    113     }
    114     // No idea what's going on. Die!
    115     CHECK(false);
    116   }
    117   return hbitmap;
    118 }
    119 
    120 PlatformBitmapPixelRef::PlatformBitmapPixelRef(HBITMAP bitmap_handle,
    121                                                void* pixels)
    122     : bitmap_handle_(bitmap_handle),
    123       pixels_(pixels) {
    124   setPreLocked(pixels, NULL);
    125 }
    126 
    127 PlatformBitmapPixelRef::~PlatformBitmapPixelRef() {
    128   if (bitmap_handle_)
    129     DeleteObject(bitmap_handle_);
    130 }
    131 
    132 void* PlatformBitmapPixelRef::onLockPixels(SkColorTable** color_table) {
    133   *color_table = NULL;
    134   return pixels_;
    135 }
    136 
    137 void PlatformBitmapPixelRef::onUnlockPixels() {
    138   // Nothing to do.
    139   return;
    140 }
    141 
    142 }  // namespace
    143 
    144 namespace skia {
    145 
    146 HDC BitmapPlatformDevice::GetBitmapDC() {
    147   if (!hdc_) {
    148     hdc_ = CreateCompatibleDC(NULL);
    149     InitializeDC(hdc_);
    150     old_hbitmap_ = static_cast<HBITMAP>(SelectObject(hdc_, hbitmap_));
    151   }
    152 
    153   LoadConfig();
    154   return hdc_;
    155 }
    156 
    157 void BitmapPlatformDevice::ReleaseBitmapDC() {
    158   SkASSERT(hdc_);
    159   SelectObject(hdc_, old_hbitmap_);
    160   DeleteDC(hdc_);
    161   hdc_ = NULL;
    162   old_hbitmap_ = NULL;
    163 }
    164 
    165 bool BitmapPlatformDevice::IsBitmapDCCreated()
    166     const {
    167   return hdc_ != NULL;
    168 }
    169 
    170 
    171 void BitmapPlatformDevice::SetMatrixClip(
    172     const SkMatrix& transform,
    173     const SkRegion& region) {
    174   transform_ = transform;
    175   clip_region_ = region;
    176   config_dirty_ = true;
    177 }
    178 
    179 void BitmapPlatformDevice::LoadConfig() {
    180   if (!config_dirty_ || !hdc_)
    181     return;  // Nothing to do.
    182   config_dirty_ = false;
    183 
    184   // Transform.
    185   LoadTransformToDC(hdc_, transform_);
    186   LoadClippingRegionToDC(hdc_, clip_region_, transform_);
    187 }
    188 
    189 // We use this static factory function instead of the regular constructor so
    190 // that we can create the pixel data before calling the constructor. This is
    191 // required so that we can call the base class' constructor with the pixel
    192 // data.
    193 BitmapPlatformDevice* BitmapPlatformDevice::Create(
    194     int width,
    195     int height,
    196     bool is_opaque,
    197     HANDLE shared_section) {
    198 
    199   void* data;
    200   HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
    201                                   &data);
    202   if (!hbitmap)
    203     return NULL;
    204 
    205   SkBitmap bitmap;
    206   bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
    207                    is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    208   RefPtr<SkPixelRef> pixel_ref = AdoptRef(new PlatformBitmapPixelRef(hbitmap,
    209                                                                      data));
    210   bitmap.setPixelRef(pixel_ref.get());
    211 
    212 #ifndef NDEBUG
    213   // If we were given data, then don't clobber it!
    214   if (!shared_section && is_opaque)
    215     // To aid in finding bugs, we set the background color to something
    216     // obviously wrong so it will be noticable when it is not cleared
    217     bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
    218 #endif
    219 
    220   // The device object will take ownership of the HBITMAP. The initial refcount
    221   // of the data object will be 1, which is what the constructor expects.
    222   return new BitmapPlatformDevice(hbitmap, bitmap);
    223 }
    224 
    225 // static
    226 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    227                                                    bool is_opaque) {
    228   return Create(width, height, is_opaque, NULL);
    229 }
    230 
    231 // static
    232 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
    233                                                            int height,
    234                                                            bool is_opaque) {
    235   BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height,
    236                                                               is_opaque);
    237   if (device && !is_opaque)
    238     device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
    239   return device;
    240 }
    241 
    242 // The device will own the HBITMAP, which corresponds to also owning the pixel
    243 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
    244 BitmapPlatformDevice::BitmapPlatformDevice(
    245     HBITMAP hbitmap,
    246     const SkBitmap& bitmap)
    247     : SkBitmapDevice(bitmap),
    248       hbitmap_(hbitmap),
    249       old_hbitmap_(NULL),
    250       hdc_(NULL),
    251       config_dirty_(true),  // Want to load the config next time.
    252       transform_(SkMatrix::I()) {
    253   // The data object is already ref'ed for us by create().
    254   SkDEBUGCODE(begin_paint_count_ = 0);
    255   SetPlatformDevice(this, this);
    256   // Initialize the clip region to the entire bitmap.
    257   BITMAP bitmap_data;
    258   if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
    259     SkIRect rect;
    260     rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
    261     clip_region_ = SkRegion(rect);
    262   }
    263 }
    264 
    265 BitmapPlatformDevice::~BitmapPlatformDevice() {
    266   SkASSERT(begin_paint_count_ == 0);
    267   if (hdc_)
    268     ReleaseBitmapDC();
    269 }
    270 
    271 HDC BitmapPlatformDevice::BeginPlatformPaint() {
    272   SkDEBUGCODE(begin_paint_count_++);
    273   return GetBitmapDC();
    274 }
    275 
    276 void BitmapPlatformDevice::EndPlatformPaint() {
    277   SkASSERT(begin_paint_count_--);
    278   PlatformDevice::EndPlatformPaint();
    279 }
    280 
    281 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
    282                                          const SkRegion& region,
    283                                          const SkClipStack&) {
    284   SetMatrixClip(transform, region);
    285 }
    286 
    287 void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y,
    288                                                const RECT* src_rect) {
    289   bool created_dc = !IsBitmapDCCreated();
    290   HDC source_dc = BeginPlatformPaint();
    291 
    292   RECT temp_rect;
    293   if (!src_rect) {
    294     temp_rect.left = 0;
    295     temp_rect.right = width();
    296     temp_rect.top = 0;
    297     temp_rect.bottom = height();
    298     src_rect = &temp_rect;
    299   }
    300 
    301   int copy_width = src_rect->right - src_rect->left;
    302   int copy_height = src_rect->bottom - src_rect->top;
    303 
    304   // We need to reset the translation for our bitmap or (0,0) won't be in the
    305   // upper left anymore
    306   SkMatrix identity;
    307   identity.reset();
    308 
    309   LoadTransformToDC(source_dc, identity);
    310   if (isOpaque()) {
    311     BitBlt(dc,
    312            x,
    313            y,
    314            copy_width,
    315            copy_height,
    316            source_dc,
    317            src_rect->left,
    318            src_rect->top,
    319            SRCCOPY);
    320   } else {
    321     SkASSERT(copy_width != 0 && copy_height != 0);
    322     BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
    323     GdiAlphaBlend(dc,
    324                   x,
    325                   y,
    326                   copy_width,
    327                   copy_height,
    328                   source_dc,
    329                   src_rect->left,
    330                   src_rect->top,
    331                   copy_width,
    332                   copy_height,
    333                   blend_function);
    334   }
    335   LoadTransformToDC(source_dc, transform_);
    336 
    337   EndPlatformPaint();
    338   if (created_dc)
    339     ReleaseBitmapDC();
    340 }
    341 
    342 const SkBitmap& BitmapPlatformDevice::onAccessBitmap() {
    343   // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
    344   // operation has occurred on our DC.
    345   if (IsBitmapDCCreated())
    346     GdiFlush();
    347   return SkBitmapDevice::onAccessBitmap();
    348 }
    349 
    350 SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
    351     SkBitmap::Config config, int width, int height, bool isOpaque, Usage) {
    352   SkASSERT(config == SkBitmap::kARGB_8888_Config);
    353   return BitmapPlatformDevice::CreateAndClear(width, height, isOpaque);
    354 }
    355 
    356 // PlatformCanvas impl
    357 
    358 SkCanvas* CreatePlatformCanvas(int width,
    359                                int height,
    360                                bool is_opaque,
    361                                HANDLE shared_section,
    362                                OnFailureType failureType) {
    363   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    364       BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
    365   return CreateCanvas(dev, failureType);
    366 }
    367 
    368 // Port of PlatformBitmap to win
    369 
    370 PlatformBitmap::~PlatformBitmap() {
    371   if (surface_) {
    372     if (platform_extra_)
    373       SelectObject(surface_, reinterpret_cast<HGDIOBJ>(platform_extra_));
    374     DeleteDC(surface_);
    375   }
    376 }
    377 
    378 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
    379   void* data;
    380   HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data);
    381   if (!hbitmap)
    382     return false;
    383 
    384   surface_ = CreateCompatibleDC(NULL);
    385   InitializeDC(surface_);
    386   // When the memory DC is created, its display surface is exactly one
    387   // monochrome pixel wide and one monochrome pixel high. Save this object
    388   // off, we'll restore it just before deleting the memory DC.
    389   HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap);
    390   platform_extra_ = reinterpret_cast<intptr_t>(stock_bitmap);
    391 
    392   bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0,
    393                     is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    394   // PlatformBitmapPixelRef takes ownership of |hbitmap|.
    395   RefPtr<SkPixelRef> pixel_ref = AdoptRef(new PlatformBitmapPixelRef(hbitmap,
    396                                                                      data));
    397   bitmap_.setPixelRef(pixel_ref.get());
    398   bitmap_.lockPixels();
    399 
    400   return true;
    401 }
    402 
    403 }  // namespace skia
    404