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/debug/gdi_debug_util_win.h"
      9 #include "base/logging.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 HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
     20                              HANDLE shared_section, void** data) {
     21   // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
     22   // just create a minimal bitmap
     23   if ((width == 0) || (height == 0)) {
     24     width = 1;
     25     height = 1;
     26   }
     27 
     28   BITMAPINFOHEADER hdr = {0};
     29   hdr.biSize = sizeof(BITMAPINFOHEADER);
     30   hdr.biWidth = width;
     31   hdr.biHeight = -height;  // minus means top-down bitmap
     32   hdr.biPlanes = 1;
     33   hdr.biBitCount = 32;
     34   hdr.biCompression = BI_RGB;  // no compression
     35   hdr.biSizeImage = 0;
     36   hdr.biXPelsPerMeter = 1;
     37   hdr.biYPelsPerMeter = 1;
     38   hdr.biClrUsed = 0;
     39   hdr.biClrImportant = 0;
     40 
     41   HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
     42                                      0, data, shared_section, 0);
     43 
     44 #if !defined(_WIN64)
     45   // If this call fails, we're gonna crash hard. Try to get some useful
     46   // information out before we crash for post-mortem analysis.
     47   if (!hbitmap)
     48     base::debug::GDIBitmapAllocFailure(&hdr, shared_section);
     49 #endif
     50 
     51   return hbitmap;
     52 }
     53 
     54 }  // namespace
     55 
     56 namespace skia {
     57 
     58 HDC BitmapPlatformDevice::GetBitmapDC() {
     59   if (!hdc_) {
     60     hdc_ = CreateCompatibleDC(NULL);
     61     InitializeDC(hdc_);
     62     old_hbitmap_ = static_cast<HBITMAP>(SelectObject(hdc_, hbitmap_));
     63   }
     64 
     65   LoadConfig();
     66   return hdc_;
     67 }
     68 
     69 void BitmapPlatformDevice::ReleaseBitmapDC() {
     70   SkASSERT(hdc_);
     71   SelectObject(hdc_, old_hbitmap_);
     72   DeleteDC(hdc_);
     73   hdc_ = NULL;
     74   old_hbitmap_ = NULL;
     75 }
     76 
     77 bool BitmapPlatformDevice::IsBitmapDCCreated()
     78     const {
     79   return hdc_ != NULL;
     80 }
     81 
     82 
     83 void BitmapPlatformDevice::SetMatrixClip(
     84     const SkMatrix& transform,
     85     const SkRegion& region) {
     86   transform_ = transform;
     87   clip_region_ = region;
     88   config_dirty_ = true;
     89 }
     90 
     91 void BitmapPlatformDevice::LoadConfig() {
     92   if (!config_dirty_ || !hdc_)
     93     return;  // Nothing to do.
     94   config_dirty_ = false;
     95 
     96   // Transform.
     97   LoadTransformToDC(hdc_, transform_);
     98   LoadClippingRegionToDC(hdc_, clip_region_, transform_);
     99 }
    100 
    101 static void DeleteHBitmapCallback(void* addr, void* context) {
    102   DeleteObject(static_cast<HBITMAP>(context));
    103 }
    104 
    105 static bool InstallHBitmapPixels(SkBitmap* bitmap, int width, int height,
    106                                  bool is_opaque, void* data, HBITMAP hbitmap) {
    107   const SkAlphaType at = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
    108   const SkImageInfo info = SkImageInfo::MakeN32(width, height, at);
    109   const size_t rowBytes = info.minRowBytes();
    110   SkColorTable* color_table = NULL;
    111   return bitmap->installPixels(info, data, rowBytes, color_table,
    112                                DeleteHBitmapCallback, hbitmap);
    113 }
    114 
    115 // We use this static factory function instead of the regular constructor so
    116 // that we can create the pixel data before calling the constructor. This is
    117 // required so that we can call the base class' constructor with the pixel
    118 // data.
    119 BitmapPlatformDevice* BitmapPlatformDevice::Create(
    120     int width,
    121     int height,
    122     bool is_opaque,
    123     HANDLE shared_section) {
    124 
    125   void* data;
    126   HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
    127                                   &data);
    128   if (!hbitmap)
    129     return NULL;
    130 
    131   SkBitmap bitmap;
    132   if (!InstallHBitmapPixels(&bitmap, width, height, is_opaque, data, hbitmap))
    133     return NULL;
    134 
    135 #ifndef NDEBUG
    136   // If we were given data, then don't clobber it!
    137   if (!shared_section && is_opaque)
    138     // To aid in finding bugs, we set the background color to something
    139     // obviously wrong so it will be noticable when it is not cleared
    140     bitmap.eraseARGB(255, 0, 255, 128);  // bright bluish green
    141 #endif
    142 
    143   // The device object will take ownership of the HBITMAP. The initial refcount
    144   // of the data object will be 1, which is what the constructor expects.
    145   return new BitmapPlatformDevice(hbitmap, bitmap);
    146 }
    147 
    148 // static
    149 BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
    150                                                    bool is_opaque) {
    151   return Create(width, height, is_opaque, NULL);
    152 }
    153 
    154 // static
    155 BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
    156                                                            int height,
    157                                                            bool is_opaque) {
    158   BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height,
    159                                                               is_opaque);
    160   if (device && !is_opaque)
    161     device->clear(0);
    162   return device;
    163 }
    164 
    165 // The device will own the HBITMAP, which corresponds to also owning the pixel
    166 // data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
    167 BitmapPlatformDevice::BitmapPlatformDevice(
    168     HBITMAP hbitmap,
    169     const SkBitmap& bitmap)
    170     : SkBitmapDevice(bitmap),
    171       hbitmap_(hbitmap),
    172       old_hbitmap_(NULL),
    173       hdc_(NULL),
    174       config_dirty_(true),  // Want to load the config next time.
    175       transform_(SkMatrix::I()) {
    176   // The data object is already ref'ed for us by create().
    177   SkDEBUGCODE(begin_paint_count_ = 0);
    178   SetPlatformDevice(this, this);
    179   // Initialize the clip region to the entire bitmap.
    180   BITMAP bitmap_data;
    181   if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
    182     SkIRect rect;
    183     rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
    184     clip_region_ = SkRegion(rect);
    185   }
    186 }
    187 
    188 BitmapPlatformDevice::~BitmapPlatformDevice() {
    189   SkASSERT(begin_paint_count_ == 0);
    190   if (hdc_)
    191     ReleaseBitmapDC();
    192 }
    193 
    194 HDC BitmapPlatformDevice::BeginPlatformPaint() {
    195   SkDEBUGCODE(begin_paint_count_++);
    196   return GetBitmapDC();
    197 }
    198 
    199 void BitmapPlatformDevice::EndPlatformPaint() {
    200   SkASSERT(begin_paint_count_--);
    201   PlatformDevice::EndPlatformPaint();
    202 }
    203 
    204 void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
    205                                          const SkRegion& region,
    206                                          const SkClipStack&) {
    207   SetMatrixClip(transform, region);
    208 }
    209 
    210 void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y,
    211                                                const RECT* src_rect) {
    212   bool created_dc = !IsBitmapDCCreated();
    213   HDC source_dc = BeginPlatformPaint();
    214 
    215   RECT temp_rect;
    216   if (!src_rect) {
    217     temp_rect.left = 0;
    218     temp_rect.right = width();
    219     temp_rect.top = 0;
    220     temp_rect.bottom = height();
    221     src_rect = &temp_rect;
    222   }
    223 
    224   int copy_width = src_rect->right - src_rect->left;
    225   int copy_height = src_rect->bottom - src_rect->top;
    226 
    227   // We need to reset the translation for our bitmap or (0,0) won't be in the
    228   // upper left anymore
    229   SkMatrix identity;
    230   identity.reset();
    231 
    232   LoadTransformToDC(source_dc, identity);
    233   if (isOpaque()) {
    234     BitBlt(dc,
    235            x,
    236            y,
    237            copy_width,
    238            copy_height,
    239            source_dc,
    240            src_rect->left,
    241            src_rect->top,
    242            SRCCOPY);
    243   } else {
    244     SkASSERT(copy_width != 0 && copy_height != 0);
    245     BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
    246     GdiAlphaBlend(dc,
    247                   x,
    248                   y,
    249                   copy_width,
    250                   copy_height,
    251                   source_dc,
    252                   src_rect->left,
    253                   src_rect->top,
    254                   copy_width,
    255                   copy_height,
    256                   blend_function);
    257   }
    258   LoadTransformToDC(source_dc, transform_);
    259 
    260   EndPlatformPaint();
    261   if (created_dc)
    262     ReleaseBitmapDC();
    263 }
    264 
    265 const SkBitmap& BitmapPlatformDevice::onAccessBitmap() {
    266   // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
    267   // operation has occurred on our DC.
    268   if (IsBitmapDCCreated())
    269     GdiFlush();
    270   return SkBitmapDevice::onAccessBitmap();
    271 }
    272 
    273 SkBaseDevice* BitmapPlatformDevice::onCreateDevice(const SkImageInfo& info,
    274                                                    Usage /*usage*/) {
    275   SkASSERT(info.colorType() == kPMColor_SkColorType);
    276   return BitmapPlatformDevice::CreateAndClear(info.width(), info.height(),
    277                                               info.isOpaque());
    278 }
    279 
    280 // PlatformCanvas impl
    281 
    282 SkCanvas* CreatePlatformCanvas(int width,
    283                                int height,
    284                                bool is_opaque,
    285                                HANDLE shared_section,
    286                                OnFailureType failureType) {
    287   skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
    288       BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
    289   return CreateCanvas(dev, failureType);
    290 }
    291 
    292 // Port of PlatformBitmap to win
    293 
    294 PlatformBitmap::~PlatformBitmap() {
    295   if (surface_) {
    296     if (platform_extra_)
    297       SelectObject(surface_, reinterpret_cast<HGDIOBJ>(platform_extra_));
    298     DeleteDC(surface_);
    299   }
    300 }
    301 
    302 bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
    303   void* data;
    304   HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data);
    305   if (!hbitmap)
    306     return false;
    307 
    308   surface_ = CreateCompatibleDC(NULL);
    309   InitializeDC(surface_);
    310   // When the memory DC is created, its display surface is exactly one
    311   // monochrome pixel wide and one monochrome pixel high. Save this object
    312   // off, we'll restore it just before deleting the memory DC.
    313   HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap);
    314   platform_extra_ = reinterpret_cast<intptr_t>(stock_bitmap);
    315 
    316   if (!InstallHBitmapPixels(&bitmap_, width, height, is_opaque, data, hbitmap))
    317     return false;
    318   bitmap_.lockPixels();
    319 
    320   return true;
    321 }
    322 
    323 }  // namespace skia
    324