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