tor-browser

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

DataSurfaceHelpers.cpp (11363B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <cstring>
      8 
      9 #include "2D.h"
     10 #include "DataSurfaceHelpers.h"
     11 #include "Logging.h"
     12 #include "Swizzle.h"
     13 #include "Tools.h"
     14 
     15 namespace mozilla {
     16 namespace gfx {
     17 
     18 int32_t StrideForFormatAndWidth(SurfaceFormat aFormat, int32_t aWidth) {
     19  MOZ_ASSERT(aFormat <= SurfaceFormat::UNKNOWN);
     20  MOZ_ASSERT(aWidth > 0);
     21 
     22  // There's nothing special about this alignment, other than that it's what
     23  // cairo_format_stride_for_width uses.
     24  static const int32_t alignment = sizeof(int32_t);
     25 
     26  const int32_t bpp = BytesPerPixel(aFormat);
     27 
     28  if (aWidth >= (INT32_MAX - alignment) / bpp) {
     29    return -1;  // too big
     30  }
     31 
     32  return (bpp * aWidth + alignment - 1) & ~(alignment - 1);
     33 }
     34 
     35 already_AddRefed<DataSourceSurface> CreateDataSourceSurfaceFromData(
     36    const IntSize& aSize, SurfaceFormat aFormat, const uint8_t* aData,
     37    int32_t aDataStride) {
     38  RefPtr<DataSourceSurface> srcSurface =
     39      Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
     40                                               aDataStride, aSize, aFormat);
     41  RefPtr<DataSourceSurface> destSurface =
     42      Factory::CreateDataSourceSurface(aSize, aFormat, false);
     43 
     44  if (!srcSurface || !destSurface) {
     45    return nullptr;
     46  }
     47 
     48  if (CopyRect(srcSurface, destSurface,
     49               IntRect(IntPoint(), srcSurface->GetSize()), IntPoint())) {
     50    return destSurface.forget();
     51  }
     52 
     53  return nullptr;
     54 }
     55 
     56 already_AddRefed<DataSourceSurface> CreateDataSourceSurfaceWithStrideFromData(
     57    const IntSize& aSize, SurfaceFormat aFormat, int32_t aStride,
     58    const uint8_t* aData, int32_t aDataStride) {
     59  RefPtr<DataSourceSurface> srcSurface =
     60      Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
     61                                               aDataStride, aSize, aFormat);
     62  RefPtr<DataSourceSurface> destSurface =
     63      Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, aStride,
     64                                                 false);
     65 
     66  if (!srcSurface || !destSurface) {
     67    return nullptr;
     68  }
     69 
     70  if (CopyRect(srcSurface, destSurface,
     71               IntRect(IntPoint(), srcSurface->GetSize()), IntPoint())) {
     72    return destSurface.forget();
     73  }
     74 
     75  return nullptr;
     76 }
     77 
     78 uint8_t* DataAtOffset(DataSourceSurface* aSurface,
     79                      const DataSourceSurface::MappedSurface* aMap,
     80                      IntPoint aPoint) {
     81  if (!SurfaceContainsPoint(aSurface, aPoint)) {
     82    MOZ_CRASH("GFX: sample position needs to be inside surface!");
     83  }
     84 
     85  MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
     86             "surface size overflows - this should have been prevented when "
     87             "the surface was created");
     88 
     89  uint8_t* data =
     90      aMap->mData + size_t(aPoint.y) * size_t(aMap->mStride) +
     91      size_t(aPoint.x) * size_t(BytesPerPixel(aSurface->GetFormat()));
     92 
     93  if (data < aMap->mData) {
     94    MOZ_CRASH("GFX: out-of-range data access");
     95  }
     96 
     97  return data;
     98 }
     99 
    100 // This check is safe against integer overflow.
    101 bool SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint) {
    102  IntSize size = aSurface->GetSize();
    103  return aPoint.x >= 0 && aPoint.x < size.width && aPoint.y >= 0 &&
    104         aPoint.y < size.height;
    105 }
    106 
    107 void CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst,
    108                                  IntSize aSrcSize, int32_t aSrcStride,
    109                                  int32_t aBytesPerPixel) {
    110  CheckedInt<size_t> packedStride(aBytesPerPixel);
    111  packedStride *= aSrcSize.width;
    112  if (!packedStride.isValid()) {
    113    MOZ_ASSERT(false, "Invalid stride");
    114    return;
    115  }
    116 
    117  CheckedInt<size_t> totalSize(aSrcStride);
    118  totalSize *= aSrcSize.height;
    119  if (!totalSize.isValid()) {
    120    MOZ_ASSERT(false, "Invalid surface size");
    121    return;
    122  }
    123 
    124  if (size_t(aSrcStride) == packedStride.value()) {
    125    // aSrc is already packed, so we can copy with a single memcpy.
    126    memcpy(aDst, aSrc, totalSize.value());
    127  } else {
    128    // memcpy one row at a time.
    129    for (int row = 0; row < aSrcSize.height; ++row) {
    130      memcpy(aDst, aSrc, packedStride.value());
    131      aSrc += aSrcStride;
    132      aDst += packedStride.value();
    133    }
    134  }
    135 }
    136 
    137 UniquePtr<uint8_t[]> SurfaceToPackedBGRA(DataSourceSurface* aSurface) {
    138  SurfaceFormat format = aSurface->GetFormat();
    139  if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
    140    return nullptr;
    141  }
    142 
    143  IntSize size = aSurface->GetSize();
    144  if (size.width < 0 || size.width >= INT32_MAX / 4) {
    145    return nullptr;
    146  }
    147  int32_t stride = size.width * 4;
    148  CheckedInt<size_t> bufferSize =
    149      CheckedInt<size_t>(stride) * CheckedInt<size_t>(size.height);
    150  if (!bufferSize.isValid()) {
    151    return nullptr;
    152  }
    153  UniquePtr<uint8_t[]> imageBuffer(new (std::nothrow)
    154                                       uint8_t[bufferSize.value()]);
    155  if (!imageBuffer) {
    156    return nullptr;
    157  }
    158 
    159  DataSourceSurface::MappedSurface map;
    160  if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
    161    return nullptr;
    162  }
    163 
    164  CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size, map.mStride,
    165                               4);
    166 
    167  aSurface->Unmap();
    168 
    169  if (format == SurfaceFormat::B8G8R8X8) {
    170    // Convert BGRX to BGRA by setting a to 255.
    171    SwizzleData(imageBuffer.get(), stride, SurfaceFormat::X8R8G8B8_UINT32,
    172                imageBuffer.get(), stride, SurfaceFormat::A8R8G8B8_UINT32,
    173                size);
    174  }
    175 
    176  return imageBuffer;
    177 }
    178 
    179 uint8_t* SurfaceToPackedBGR(DataSourceSurface* aSurface) {
    180  SurfaceFormat format = aSurface->GetFormat();
    181  MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
    182 
    183  if (format != SurfaceFormat::B8G8R8X8) {
    184    // To support B8G8R8A8 we'd need to un-pre-multiply alpha
    185    return nullptr;
    186  }
    187 
    188  IntSize size = aSurface->GetSize();
    189  if (size.width < 0 || size.width >= INT32_MAX / 3) {
    190    return nullptr;
    191  }
    192  int32_t stride = size.width * 3;
    193  CheckedInt<size_t> bufferSize =
    194      CheckedInt<size_t>(stride) * CheckedInt<size_t>(size.height);
    195  if (!bufferSize.isValid()) {
    196    return nullptr;
    197  }
    198  uint8_t* imageBuffer = new (std::nothrow) uint8_t[bufferSize.value()];
    199  if (!imageBuffer) {
    200    return nullptr;
    201  }
    202 
    203  DataSourceSurface::MappedSurface map;
    204  if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
    205    delete[] imageBuffer;
    206    return nullptr;
    207  }
    208 
    209  SwizzleData(map.mData, map.mStride, SurfaceFormat::B8G8R8X8, imageBuffer,
    210              stride, SurfaceFormat::B8G8R8, size);
    211 
    212  aSurface->Unmap();
    213 
    214  return imageBuffer;
    215 }
    216 
    217 void ClearDataSourceSurface(DataSourceSurface* aSurface) {
    218  DataSourceSurface::MappedSurface map;
    219  if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
    220    MOZ_ASSERT(false, "Failed to map DataSourceSurface");
    221    return;
    222  }
    223 
    224  // We avoid writing into the gaps between the rows here since we can't be
    225  // sure that some drivers don't use those bytes.
    226 
    227  uint32_t width = aSurface->GetSize().width;
    228  uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat());
    229  uint8_t* row = map.mData;
    230  // converting to size_t here because otherwise the temporaries can overflow
    231  // and we can end up with |end| being a bad address!
    232  uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height);
    233 
    234  while (row != end) {
    235    memset(row, 0, bytesPerRow);
    236    row += map.mStride;
    237  }
    238 
    239  aSurface->Unmap();
    240 }
    241 
    242 size_t BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight,
    243                                     int32_t aExtraBytes) {
    244  if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) {
    245    return 0;
    246  }
    247 
    248  // We limit the length returned to values that can be represented by int32_t
    249  // because we don't want to allocate buffers any bigger than that. This
    250  // allows for a buffer size of over 2 GiB which is already rediculously
    251  // large and will make the process janky. (Note the choice of the signed type
    252  // is deliberate because we specifically don't want the returned value to
    253  // overflow if someone stores the buffer length in an int32_t variable.)
    254 
    255  CheckedInt32 requiredBytes =
    256      CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes);
    257  if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
    258    gfxWarning() << "Buffer size too big; returning zero " << aStride << ", "
    259                 << aHeight << ", " << aExtraBytes;
    260    return 0;
    261  }
    262  return requiredBytes.value();
    263 }
    264 
    265 size_t BufferSizeFromDimensions(int32_t aWidth, int32_t aHeight, int32_t aDepth,
    266                                int32_t aExtraBytes) {
    267  if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aWidth <= 0) ||
    268      MOZ_UNLIKELY(aDepth <= 0)) {
    269    return 0;
    270  }
    271 
    272  // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
    273 
    274  CheckedInt32 requiredBytes =
    275      CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) +
    276      CheckedInt32(aExtraBytes);
    277  if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
    278    gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", "
    279                 << aHeight << ", " << aDepth << ", " << aExtraBytes;
    280    return 0;
    281  }
    282  return requiredBytes.value();
    283 }
    284 
    285 /**
    286 * aSrcRect: Rect relative to the aSrc surface
    287 * aDestPoint: Point inside aDest surface
    288 *
    289 * aSrcRect and aDestPoint are in internal local coordinates.
    290 * i.e. locations of pixels and not in the same coordinate space
    291 * as aSrc->GetRect()
    292 */
    293 bool CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
    294              IntRect aSrcRect, IntPoint aDestPoint) {
    295  if (aSrcRect.Overflows() ||
    296      IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
    297    MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
    298  }
    299 
    300  MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
    301                     "GFX: different surface formats");
    302  MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
    303                     "GFX: source rect too big for source surface");
    304  MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize())
    305                         .Contains(IntRect(aDestPoint, aSrcRect.Size())),
    306                     "GFX: dest surface too small");
    307 
    308  if (aSrcRect.IsEmpty()) {
    309    return false;
    310  }
    311 
    312  DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ);
    313  DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE);
    314  if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) {
    315    return false;
    316  }
    317 
    318  uint8_t* sourceData =
    319      DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft());
    320  uint8_t* destData =
    321      DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint);
    322 
    323  SwizzleData(sourceData, srcMap.GetStride(), aSrc->GetFormat(), destData,
    324              destMap.GetStride(), aDest->GetFormat(), aSrcRect.Size());
    325 
    326  return true;
    327 }
    328 
    329 already_AddRefed<DataSourceSurface> CreateDataSourceSurfaceByCloning(
    330    DataSourceSurface* aSource) {
    331  RefPtr<DataSourceSurface> copy = Factory::CreateDataSourceSurface(
    332      aSource->GetSize(), aSource->GetFormat(), true);
    333  if (copy) {
    334    CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()),
    335             IntPoint());
    336  }
    337  return copy.forget();
    338 }
    339 
    340 }  // namespace gfx
    341 }  // namespace mozilla