| // Copyright (c) 2017 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 <stddef.h> |
| #include <string.h> |
| |
| #include "base/debug/gdi_debug_util_win.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/logging.h" |
| #include "base/win/win_util.h" |
| #include "skia/ext/platform_canvas.h" |
| #include "skia/ext/skia_utils_win.h" |
| #include "third_party/skia/include/core/SkMatrix.h" |
| #include "third_party/skia/include/core/SkPath.h" |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| |
| namespace { |
| |
| struct HDCContextRec { |
| HDC hdc_; |
| HBITMAP prev_bitmap_; |
| }; |
| |
| static void DeleteHDCCallback(void*, void* context) { |
| DCHECK_NE(context, nullptr); |
| const HDCContextRec* rec = static_cast<const HDCContextRec*>(context); |
| |
| // Must select back in the old bitmap before we delete the hdc, and so we can |
| // recover the new_bitmap that we allocated, so we can delete it. |
| HBITMAP new_bitmap = |
| static_cast<HBITMAP>(SelectObject(rec->hdc_, rec->prev_bitmap_)); |
| bool success = DeleteObject(new_bitmap); |
| DCHECK(success); |
| success = DeleteDC(rec->hdc_); |
| DCHECK(success); |
| delete rec; |
| } |
| |
| // Allocate the layer and fill in the fields for the Rec, or return false |
| // on error. |
| static bool Create(int width, |
| int height, |
| bool is_opaque, |
| HANDLE shared_section, |
| bool do_clear, |
| SkRasterHandleAllocator::Rec* rec) { |
| void* pixels; |
| HBITMAP new_bitmap = |
| skia::CreateHBitmap(width, height, is_opaque, shared_section, &pixels); |
| if (!new_bitmap) { |
| LOG(ERROR) << "CreateHBitmap failed"; |
| return false; |
| } |
| |
| size_t row_bytes = skia::PlatformCanvasStrideForWidth(width); |
| if (do_clear) |
| memset(pixels, 0, row_bytes * height); |
| |
| HDC hdc = CreateCompatibleDC(nullptr); |
| if (!hdc) { |
| DeleteObject(new_bitmap); |
| return false; |
| } |
| SetGraphicsMode(hdc, GM_ADVANCED); |
| |
| HBITMAP prev_bitmap = static_cast<HBITMAP>(SelectObject(hdc, new_bitmap)); |
| DCHECK(prev_bitmap); |
| |
| rec->fReleaseProc = DeleteHDCCallback; |
| rec->fReleaseCtx = new HDCContextRec{hdc, prev_bitmap}; |
| rec->fPixels = pixels; |
| rec->fRowBytes = row_bytes; |
| rec->fHandle = hdc; |
| return true; |
| } |
| |
| /** |
| * Subclass of SkRasterHandleAllocator that returns an HDC as its "handle". |
| */ |
| class GDIAllocator : public SkRasterHandleAllocator { |
| public: |
| GDIAllocator() {} |
| |
| bool allocHandle(const SkImageInfo& info, Rec* rec) override { |
| SkASSERT(info.colorType() == kN32_SkColorType); |
| return Create(info.width(), info.height(), info.isOpaque(), nullptr, |
| !info.isOpaque(), rec); |
| } |
| |
| void updateHandle(Handle handle, |
| const SkMatrix& ctm, |
| const SkIRect& clip_bounds) override { |
| HDC hdc = static_cast<HDC>(handle); |
| skia::LoadTransformToDC(hdc, ctm); |
| |
| HRGN hrgn = CreateRectRgnIndirect(&skia::SkIRectToRECT(clip_bounds)); |
| int result = SelectClipRgn(hdc, hrgn); |
| DCHECK(result != ERROR); |
| result = DeleteObject(hrgn); |
| DCHECK(result != 0); |
| } |
| }; |
| |
| void unmap_view_proc(void* pixels, void*) { |
| UnmapViewOfFile(pixels); |
| } |
| |
| } // namespace |
| |
| namespace skia { |
| |
| std::unique_ptr<SkCanvas> CreatePlatformCanvasWithSharedSection( |
| int width, |
| int height, |
| bool is_opaque, |
| HANDLE shared_section, |
| OnFailureType failure_type) { |
| SkAlphaType alpha = is_opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; |
| SkImageInfo info = SkImageInfo::MakeN32(width, height, alpha); |
| size_t row_bytes = PlatformCanvasStrideForWidth(width); |
| |
| // This function contains an implementation of a Skia platform bitmap for |
| // drawing and compositing graphics. The original implementation uses Windows |
| // GDI to create the backing bitmap memory, however it's possible for a |
| // process to not have access to GDI which will cause this code to fail. It's |
| // possible to detect when GDI is unavailable and instead directly map the |
| // shared memory as the bitmap. |
| if (base::win::IsUser32AndGdi32Available()) { |
| SkRasterHandleAllocator::Rec rec; |
| if (Create(width, height, is_opaque, shared_section, false, &rec)) |
| return SkRasterHandleAllocator::MakeCanvas( |
| std::make_unique<GDIAllocator>(), info, &rec); |
| } else { |
| DCHECK(shared_section != NULL); |
| void* pixels = |
| MapViewOfFile(shared_section, FILE_MAP_WRITE, 0, 0, row_bytes * height); |
| if (pixels) { |
| SkBitmap bitmap; |
| if (bitmap.installPixels(info, pixels, row_bytes, unmap_view_proc, |
| nullptr)) { |
| return std::make_unique<SkCanvas>(bitmap); |
| } |
| } |
| } |
| |
| if (failure_type == CRASH_ON_FAILURE) |
| SK_CRASH(); |
| return nullptr; |
| } |
| |
| HDC GetNativeDrawingContext(SkCanvas* canvas) { |
| return canvas ? static_cast<HDC>(canvas->accessTopRasterHandle()) : nullptr; |
| } |
| |
| } // namespace skia |