tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cursor.cc (8430B)


      1 /*
      2 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/desktop_capture/win/cursor.h"
     12 
     13 #include <cstddef>
     14 #include <cstdint>
     15 #include <cstring>
     16 #include <memory>
     17 
     18 #include "modules/desktop_capture/desktop_frame.h"
     19 #include "modules/desktop_capture/desktop_geometry.h"
     20 #include "modules/desktop_capture/mouse_cursor.h"
     21 #include "modules/desktop_capture/win/scoped_gdi_object.h"
     22 #include "rtc_base/logging.h"
     23 #include "rtc_base/system/arch.h"
     24 
     25 namespace webrtc {
     26 
     27 namespace {
     28 
     29 #if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
     30 
     31 #define RGBA(r, g, b, a)                                   \
     32  ((((a) << 24) & 0xff000000) | (((b) << 16) & 0xff0000) | \
     33   (((g) << 8) & 0xff00) | ((r) & 0xff))
     34 
     35 #else  // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
     36 
     37 #define RGBA(r, g, b, a)                                   \
     38  ((((r) << 24) & 0xff000000) | (((g) << 16) & 0xff0000) | \
     39   (((b) << 8) & 0xff00) | ((a) & 0xff))
     40 
     41 #endif  // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
     42 
     43 const int kBytesPerPixel = DesktopFrame::kBytesPerPixel;
     44 
     45 // Pixel colors used when generating cursor outlines.
     46 const uint32_t kPixelRgbaBlack = RGBA(0, 0, 0, 0xff);
     47 const uint32_t kPixelRgbaWhite = RGBA(0xff, 0xff, 0xff, 0xff);
     48 const uint32_t kPixelRgbaTransparent = RGBA(0, 0, 0, 0);
     49 
     50 const uint32_t kPixelRgbWhite = RGB(0xff, 0xff, 0xff);
     51 
     52 // Expands the cursor shape to add a white outline for visibility against
     53 // dark backgrounds.
     54 void AddCursorOutline(int width, int height, uint32_t* data) {
     55  for (int y = 0; y < height; y++) {
     56    for (int x = 0; x < width; x++) {
     57      // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
     58      // neighbor pixels to see if this should be changed to an outline pixel.
     59      if (*data == kPixelRgbaTransparent) {
     60        // Change to white pixel if any neighbors (top, bottom, left, right)
     61        // are black.
     62        if ((y > 0 && data[-width] == kPixelRgbaBlack) ||
     63            (y < height - 1 && data[width] == kPixelRgbaBlack) ||
     64            (x > 0 && data[-1] == kPixelRgbaBlack) ||
     65            (x < width - 1 && data[1] == kPixelRgbaBlack)) {
     66          *data = kPixelRgbaWhite;
     67        }
     68      }
     69      data++;
     70    }
     71  }
     72 }
     73 
     74 // Premultiplies RGB components of the pixel data in the given image by
     75 // the corresponding alpha components.
     76 void AlphaMul(uint32_t* data, int width, int height) {
     77  static_assert(sizeof(uint32_t) == kBytesPerPixel,
     78                "size of uint32 should be the number of bytes per pixel");
     79 
     80  for (uint32_t* data_end = data + width * height; data != data_end; ++data) {
     81    RGBQUAD* from = reinterpret_cast<RGBQUAD*>(data);
     82    RGBQUAD* to = reinterpret_cast<RGBQUAD*>(data);
     83    to->rgbBlue =
     84        (static_cast<uint16_t>(from->rgbBlue) * from->rgbReserved) / 0xff;
     85    to->rgbGreen =
     86        (static_cast<uint16_t>(from->rgbGreen) * from->rgbReserved) / 0xff;
     87    to->rgbRed =
     88        (static_cast<uint16_t>(from->rgbRed) * from->rgbReserved) / 0xff;
     89  }
     90 }
     91 
     92 // Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
     93 // Returns true if non-zero alpha is found. `stride` is expressed in pixels.
     94 bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
     95  const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
     96  for (int y = 0; y < height; ++y) {
     97    for (int x = 0; x < width; ++x) {
     98      if (plane->rgbReserved != 0)
     99        return true;
    100      plane += 1;
    101    }
    102    plane += stride - width;
    103  }
    104 
    105  return false;
    106 }
    107 
    108 }  // namespace
    109 
    110 MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
    111  ICONINFO iinfo;
    112  if (!GetIconInfo(cursor, &iinfo)) {
    113    RTC_LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
    114                        << GetLastError();
    115    return NULL;
    116  }
    117 
    118  int hotspot_x = iinfo.xHotspot;
    119  int hotspot_y = iinfo.yHotspot;
    120 
    121  // Make sure the bitmaps will be freed.
    122  win::ScopedBitmap scoped_mask(iinfo.hbmMask);
    123  win::ScopedBitmap scoped_color(iinfo.hbmColor);
    124  bool is_color = iinfo.hbmColor != NULL;
    125 
    126  // Get `scoped_mask` dimensions.
    127  BITMAP bitmap_info;
    128  if (!GetObject(scoped_mask, sizeof(bitmap_info), &bitmap_info)) {
    129    RTC_LOG_F(LS_ERROR) << "Unable to get bitmap info. Error = "
    130                        << GetLastError();
    131    return NULL;
    132  }
    133 
    134  int width = bitmap_info.bmWidth;
    135  int height = bitmap_info.bmHeight;
    136  std::unique_ptr<uint32_t[]> mask_data(new uint32_t[width * height]);
    137 
    138  // Get pixel data from `scoped_mask` converting it to 32bpp along the way.
    139  // GetDIBits() sets the alpha component of every pixel to 0.
    140  BITMAPV5HEADER bmi = {0};
    141  bmi.bV5Size = sizeof(bmi);
    142  bmi.bV5Width = width;
    143  bmi.bV5Height = -height;  // request a top-down bitmap.
    144  bmi.bV5Planes = 1;
    145  bmi.bV5BitCount = kBytesPerPixel * 8;
    146  bmi.bV5Compression = BI_RGB;
    147  bmi.bV5AlphaMask = 0xff000000;
    148  bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
    149  bmi.bV5Intent = LCS_GM_BUSINESS;
    150  if (!GetDIBits(dc, scoped_mask, 0, height, mask_data.get(),
    151                 reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
    152    RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
    153                        << GetLastError();
    154    return NULL;
    155  }
    156 
    157  uint32_t* mask_plane = mask_data.get();
    158  std::unique_ptr<DesktopFrame> image(
    159      new BasicDesktopFrame(DesktopSize(width, height), FOURCC_ARGB));
    160  bool has_alpha = false;
    161 
    162  if (is_color) {
    163    image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
    164    // Get the pixels from the color bitmap.
    165    if (!GetDIBits(dc, scoped_color, 0, height, image->data(),
    166                   reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
    167      RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
    168                          << GetLastError();
    169      return NULL;
    170    }
    171 
    172    // GetDIBits() does not provide any indication whether the bitmap has alpha
    173    // channel, so we use HasAlphaChannel() below to find it out.
    174    has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
    175                                width, width, height);
    176  } else {
    177    // For non-color cursors, the mask contains both an AND and an XOR mask and
    178    // the height includes both. Thus, the width is correct, but we need to
    179    // divide by 2 to get the correct mask height.
    180    height /= 2;
    181 
    182    image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
    183 
    184    // The XOR mask becomes the color bitmap.
    185    memcpy(image->data(), mask_plane + (width * height),
    186           image->stride() * height);
    187  }
    188 
    189  // Reconstruct transparency from the mask if the color image does not has
    190  // alpha channel.
    191  if (!has_alpha) {
    192    bool add_outline = false;
    193    uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
    194    uint32_t* mask = mask_plane;
    195    for (int y = 0; y < height; y++) {
    196      for (int x = 0; x < width; x++) {
    197        // The two bitmaps combine as follows:
    198        //  mask  color   Windows Result   Our result    RGB   Alpha
    199        //   0     00      Black            Black         00    ff
    200        //   0     ff      White            White         ff    ff
    201        //   1     00      Screen           Transparent   00    00
    202        //   1     ff      Reverse-screen   Black         00    ff
    203        //
    204        // Since we don't support XOR cursors, we replace the "Reverse Screen"
    205        // with black. In this case, we also add an outline around the cursor
    206        // so that it is visible against a dark background.
    207        if (*mask == kPixelRgbWhite) {
    208          if (*dst != 0) {
    209            add_outline = true;
    210            *dst = kPixelRgbaBlack;
    211          } else {
    212            *dst = kPixelRgbaTransparent;
    213          }
    214        } else {
    215          *dst = kPixelRgbaBlack ^ *dst;
    216        }
    217 
    218        ++dst;
    219        ++mask;
    220      }
    221    }
    222    if (add_outline) {
    223      AddCursorOutline(width, height,
    224                       reinterpret_cast<uint32_t*>(image->data()));
    225    }
    226  }
    227 
    228  // Pre-multiply the resulting pixels since MouseCursor uses premultiplied
    229  // images.
    230  AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
    231 
    232  return new MouseCursor(image.release(), DesktopVector(hotspot_x, hotspot_y));
    233 }
    234 
    235 }  // namespace webrtc