Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(87)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp

Issue 2458943002: Support 2D texture sub-source uploads from HTMLImageElement. (Closed)
Patch Set: Fixed bug in computation of default image sub-rectangle. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "platform/graphics/gpu/WebGLImageConversion.h" 5 #include "platform/graphics/gpu/WebGLImageConversion.h"
6 6
7 #include "platform/graphics/ImageObserver.h" 7 #include "platform/graphics/ImageObserver.h"
8 #include "platform/graphics/cpu/arm/WebGLImageConversionNEON.h" 8 #include "platform/graphics/cpu/arm/WebGLImageConversionNEON.h"
9 #include "platform/graphics/cpu/mips/WebGLImageConversionMSA.h" 9 #include "platform/graphics/cpu/mips/WebGLImageConversionMSA.h"
10 #include "platform/graphics/cpu/x86/WebGLImageConversionSSE.h" 10 #include "platform/graphics/cpu/x86/WebGLImageConversionSSE.h"
(...skipping 2214 matching lines...) Expand 10 before | Expand all | Expand 10 after
2225 return 0; 2225 return 0;
2226 } 2226 }
2227 } 2227 }
2228 2228
2229 /* END CODE SHARED WITH MOZILLA FIREFOX */ 2229 /* END CODE SHARED WITH MOZILLA FIREFOX */
2230 2230
2231 class FormatConverter { 2231 class FormatConverter {
2232 STACK_ALLOCATED(); 2232 STACK_ALLOCATED();
2233 2233
2234 public: 2234 public:
2235 FormatConverter(unsigned width, 2235 FormatConverter(const IntRect& sourceDataSubRectangle,
2236 unsigned height,
2237 const void* srcStart, 2236 const void* srcStart,
2238 void* dstStart, 2237 void* dstStart,
2239 int srcStride, 2238 int srcStride,
2239 int srcRowOffset,
2240 int dstStride) 2240 int dstStride)
2241 : m_width(width), 2241 : m_srcSubRectangle(sourceDataSubRectangle),
2242 m_height(height),
2243 m_srcStart(srcStart), 2242 m_srcStart(srcStart),
2244 m_dstStart(dstStart), 2243 m_dstStart(dstStart),
2245 m_srcStride(srcStride), 2244 m_srcStride(srcStride),
2245 m_srcRowOffset(srcRowOffset),
2246 m_dstStride(dstStride), 2246 m_dstStride(dstStride),
2247 m_success(false) { 2247 m_success(false) {
2248 const unsigned MaxNumberOfComponents = 4; 2248 const unsigned MaxNumberOfComponents = 4;
2249 const unsigned MaxBytesPerComponent = 4; 2249 const unsigned MaxBytesPerComponent = 4;
2250 m_unpackedIntermediateSrcData = wrapArrayUnique( 2250 m_unpackedIntermediateSrcData = wrapArrayUnique(
2251 new uint8_t[m_width * MaxNumberOfComponents * MaxBytesPerComponent]); 2251 new uint8_t[m_srcSubRectangle.width() * MaxNumberOfComponents *
2252 MaxBytesPerComponent]);
2252 ASSERT(m_unpackedIntermediateSrcData.get()); 2253 ASSERT(m_unpackedIntermediateSrcData.get());
2253 } 2254 }
2254 2255
2255 void convert(WebGLImageConversion::DataFormat srcFormat, 2256 void convert(WebGLImageConversion::DataFormat srcFormat,
2256 WebGLImageConversion::DataFormat dstFormat, 2257 WebGLImageConversion::DataFormat dstFormat,
2257 WebGLImageConversion::AlphaOp); 2258 WebGLImageConversion::AlphaOp);
2258 bool Success() const { return m_success; } 2259 bool Success() const { return m_success; }
2259 2260
2260 private: 2261 private:
2261 template <WebGLImageConversion::DataFormat SrcFormat> 2262 template <WebGLImageConversion::DataFormat SrcFormat>
2262 void convert(WebGLImageConversion::DataFormat dstFormat, 2263 void convert(WebGLImageConversion::DataFormat dstFormat,
2263 WebGLImageConversion::AlphaOp); 2264 WebGLImageConversion::AlphaOp);
2264 2265
2265 template <WebGLImageConversion::DataFormat SrcFormat, 2266 template <WebGLImageConversion::DataFormat SrcFormat,
2266 WebGLImageConversion::DataFormat DstFormat> 2267 WebGLImageConversion::DataFormat DstFormat>
2267 void convert(WebGLImageConversion::AlphaOp); 2268 void convert(WebGLImageConversion::AlphaOp);
2268 2269
2269 template <WebGLImageConversion::DataFormat SrcFormat, 2270 template <WebGLImageConversion::DataFormat SrcFormat,
2270 WebGLImageConversion::DataFormat DstFormat, 2271 WebGLImageConversion::DataFormat DstFormat,
2271 WebGLImageConversion::AlphaOp alphaOp> 2272 WebGLImageConversion::AlphaOp alphaOp>
2272 void convert(); 2273 void convert();
2273 2274
2274 const unsigned m_width, m_height; 2275 const IntRect& m_srcSubRectangle;
2275 const void* const m_srcStart; 2276 const void* const m_srcStart;
2276 void* const m_dstStart; 2277 void* const m_dstStart;
2277 const int m_srcStride, m_dstStride; 2278 const int m_srcStride, m_srcRowOffset, m_dstStride;
2278 bool m_success; 2279 bool m_success;
2279 std::unique_ptr<uint8_t[]> m_unpackedIntermediateSrcData; 2280 std::unique_ptr<uint8_t[]> m_unpackedIntermediateSrcData;
2280 }; 2281 };
2281 2282
2282 void FormatConverter::convert(WebGLImageConversion::DataFormat srcFormat, 2283 void FormatConverter::convert(WebGLImageConversion::DataFormat srcFormat,
2283 WebGLImageConversion::DataFormat dstFormat, 2284 WebGLImageConversion::DataFormat dstFormat,
2284 WebGLImageConversion::AlphaOp alphaOp) { 2285 WebGLImageConversion::AlphaOp alphaOp) {
2285 #define FORMATCONVERTER_CASE_SRCFORMAT(SrcFormat) \ 2286 #define FORMATCONVERTER_CASE_SRCFORMAT(SrcFormat) \
2286 case SrcFormat: \ 2287 case SrcFormat: \
2287 return convert<SrcFormat>(dstFormat, alphaOp); 2288 return convert<SrcFormat>(dstFormat, alphaOp);
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
2445 typedef typename DataTypeForFormat<DstFormat>::Type DstType; 2446 typedef typename DataTypeForFormat<DstFormat>::Type DstType;
2446 const int IntermFormat = IntermediateFormat<DstFormat>::value; 2447 const int IntermFormat = IntermediateFormat<DstFormat>::value;
2447 typedef typename DataTypeForFormat<IntermFormat>::Type IntermType; 2448 typedef typename DataTypeForFormat<IntermFormat>::Type IntermType;
2448 const ptrdiff_t srcStrideInElements = m_srcStride / sizeof(SrcType); 2449 const ptrdiff_t srcStrideInElements = m_srcStride / sizeof(SrcType);
2449 const ptrdiff_t dstStrideInElements = m_dstStride / sizeof(DstType); 2450 const ptrdiff_t dstStrideInElements = m_dstStride / sizeof(DstType);
2450 const bool trivialUnpack = SrcFormat == IntermFormat; 2451 const bool trivialUnpack = SrcFormat == IntermFormat;
2451 const bool trivialPack = DstFormat == IntermFormat && 2452 const bool trivialPack = DstFormat == IntermFormat &&
2452 alphaOp == WebGLImageConversion::AlphaDoNothing; 2453 alphaOp == WebGLImageConversion::AlphaDoNothing;
2453 ASSERT(!trivialUnpack || !trivialPack); 2454 ASSERT(!trivialUnpack || !trivialPack);
2454 2455
2455 const SrcType* srcRowStart = static_cast<const SrcType*>(m_srcStart); 2456 const SrcType* srcRowStart =
2457 static_cast<const SrcType*>(static_cast<const void*>(
2458 static_cast<const uint8_t*>(m_srcStart) +
2459 ((m_srcStride * m_srcSubRectangle.y()) + m_srcRowOffset)));
2456 DstType* dstRowStart = static_cast<DstType*>(m_dstStart); 2460 DstType* dstRowStart = static_cast<DstType*>(m_dstStart);
2457 if (trivialUnpack) { 2461 if (trivialUnpack) {
2458 for (size_t i = 0; i < m_height; ++i) { 2462 for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
2459 pack<DstFormat, alphaOp>(srcRowStart, dstRowStart, m_width); 2463 pack<DstFormat, alphaOp>(srcRowStart, dstRowStart,
2464 m_srcSubRectangle.width());
2460 srcRowStart += srcStrideInElements; 2465 srcRowStart += srcStrideInElements;
2461 dstRowStart += dstStrideInElements; 2466 dstRowStart += dstStrideInElements;
2462 } 2467 }
2463 } else if (trivialPack) { 2468 } else if (trivialPack) {
2464 for (size_t i = 0; i < m_height; ++i) { 2469 for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
2465 unpack<SrcFormat>(srcRowStart, dstRowStart, m_width); 2470 unpack<SrcFormat>(srcRowStart, dstRowStart, m_srcSubRectangle.width());
2466 srcRowStart += srcStrideInElements; 2471 srcRowStart += srcStrideInElements;
2467 dstRowStart += dstStrideInElements; 2472 dstRowStart += dstStrideInElements;
2468 } 2473 }
2469 } else { 2474 } else {
2470 for (size_t i = 0; i < m_height; ++i) { 2475 for (int i = 0; i < m_srcSubRectangle.height(); ++i) {
2471 unpack<SrcFormat>(srcRowStart, reinterpret_cast<IntermType*>( 2476 unpack<SrcFormat>(srcRowStart, reinterpret_cast<IntermType*>(
2472 m_unpackedIntermediateSrcData.get()), 2477 m_unpackedIntermediateSrcData.get()),
2473 m_width); 2478 m_srcSubRectangle.width());
2474 pack<DstFormat, alphaOp>( 2479 pack<DstFormat, alphaOp>(
2475 reinterpret_cast<IntermType*>(m_unpackedIntermediateSrcData.get()), 2480 reinterpret_cast<IntermType*>(m_unpackedIntermediateSrcData.get()),
2476 dstRowStart, m_width); 2481 dstRowStart, m_srcSubRectangle.width());
2477 srcRowStart += srcStrideInElements; 2482 srcRowStart += srcStrideInElements;
2478 dstRowStart += dstStrideInElements; 2483 dstRowStart += dstStrideInElements;
2479 } 2484 }
2480 } 2485 }
2481 m_success = true; 2486 m_success = true;
2482 return; 2487 return;
2483 } 2488 }
2484 2489
2485 bool frameIsValid(const SkBitmap& frameBitmap) { 2490 bool frameIsValid(const SkBitmap& frameBitmap) {
2486 return !frameBitmap.isNull() && !frameBitmap.empty() && 2491 return !frameBitmap.isNull() && !frameBitmap.empty() &&
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after
2854 } 2859 }
2855 } 2860 }
2856 2861
2857 bool WebGLImageConversion::packImageData(Image* image, 2862 bool WebGLImageConversion::packImageData(Image* image,
2858 const void* pixels, 2863 const void* pixels,
2859 GLenum format, 2864 GLenum format,
2860 GLenum type, 2865 GLenum type,
2861 bool flipY, 2866 bool flipY,
2862 AlphaOp alphaOp, 2867 AlphaOp alphaOp,
2863 DataFormat sourceFormat, 2868 DataFormat sourceFormat,
2864 unsigned width, 2869 unsigned sourceImageWidth,
2865 unsigned height, 2870 unsigned sourceImageHeight,
2871 const IntRect& sourceImageSubRectangle,
2866 unsigned sourceUnpackAlignment, 2872 unsigned sourceUnpackAlignment,
2867 Vector<uint8_t>& data) { 2873 Vector<uint8_t>& data) {
2868 if (!pixels) 2874 if (!pixels)
2869 return false; 2875 return false;
2870 2876
2871 unsigned packedSize; 2877 unsigned packedSize;
2872 // Output data is tightly packed (alignment == 1). 2878 // Output data is tightly packed (alignment == 1).
2873 PixelStoreParams params; 2879 PixelStoreParams params;
2874 params.alignment = 1; 2880 params.alignment = 1;
2875 if (computeImageSizeInBytes(format, type, width, height, 1, params, 2881 if (computeImageSizeInBytes(format, type, sourceImageSubRectangle.width(),
2882 sourceImageSubRectangle.height(), 1, params,
2876 &packedSize, 0, 0) != GL_NO_ERROR) 2883 &packedSize, 0, 0) != GL_NO_ERROR)
2877 return false; 2884 return false;
2878 data.resize(packedSize); 2885 data.resize(packedSize);
2879 2886
2880 if (!packPixels(reinterpret_cast<const uint8_t*>(pixels), sourceFormat, width, 2887 if (!packPixels(reinterpret_cast<const uint8_t*>(pixels), sourceFormat,
2881 height, sourceUnpackAlignment, format, type, alphaOp, 2888 sourceImageWidth, sourceImageHeight, sourceImageSubRectangle,
2882 data.data(), flipY)) 2889 sourceUnpackAlignment, format, type, alphaOp, data.data(),
2890 flipY))
2883 return false; 2891 return false;
2884 if (ImageObserver* observer = image->getImageObserver()) 2892 if (ImageObserver* observer = image->getImageObserver())
2885 observer->didDraw(image); 2893 observer->didDraw(image);
2886 return true; 2894 return true;
2887 } 2895 }
2888 2896
2889 bool WebGLImageConversion::extractImageData(const uint8_t* imageData, 2897 bool WebGLImageConversion::extractImageData(const uint8_t* imageData,
2890 DataFormat sourceDataFormat, 2898 DataFormat sourceDataFormat,
2891 const IntSize& imageDataSize, 2899 const IntSize& imageDataSize,
2892 GLenum format, 2900 GLenum format,
2893 GLenum type, 2901 GLenum type,
2894 bool flipY, 2902 bool flipY,
2895 bool premultiplyAlpha, 2903 bool premultiplyAlpha,
2896 Vector<uint8_t>& data) { 2904 Vector<uint8_t>& data) {
2897 if (!imageData) 2905 if (!imageData)
2898 return false; 2906 return false;
2899 int width = imageDataSize.width(); 2907 int width = imageDataSize.width();
2900 int height = imageDataSize.height(); 2908 int height = imageDataSize.height();
2901 2909
2902 unsigned packedSize; 2910 unsigned packedSize;
2903 // Output data is tightly packed (alignment == 1). 2911 // Output data is tightly packed (alignment == 1).
2904 PixelStoreParams params; 2912 PixelStoreParams params;
2905 params.alignment = 1; 2913 params.alignment = 1;
2906 if (computeImageSizeInBytes(format, type, width, height, 1, params, 2914 if (computeImageSizeInBytes(format, type, width, height, 1, params,
2907 &packedSize, 0, 0) != GL_NO_ERROR) 2915 &packedSize, 0, 0) != GL_NO_ERROR)
2908 return false; 2916 return false;
2909 data.resize(packedSize); 2917 data.resize(packedSize);
2910 2918
2911 if (!packPixels(imageData, sourceDataFormat, width, height, 0, format, type, 2919 if (!packPixels(imageData, sourceDataFormat, width, height,
2920 IntRect(0, 0, width, height), 0, format, type,
2912 premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing, 2921 premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing,
2913 data.data(), flipY)) 2922 data.data(), flipY))
2914 return false; 2923 return false;
2915 2924
2916 return true; 2925 return true;
2917 } 2926 }
2918 2927
2919 bool WebGLImageConversion::extractTextureData(unsigned width, 2928 bool WebGLImageConversion::extractTextureData(unsigned width,
2920 unsigned height, 2929 unsigned height,
2921 GLenum format, 2930 GLenum format,
2922 GLenum type, 2931 GLenum type,
2923 unsigned unpackAlignment, 2932 unsigned unpackAlignment,
2924 bool flipY, 2933 bool flipY,
2925 bool premultiplyAlpha, 2934 bool premultiplyAlpha,
2926 const void* pixels, 2935 const void* pixels,
2927 Vector<uint8_t>& data) { 2936 Vector<uint8_t>& data) {
2928 // Assumes format, type, etc. have already been validated. 2937 // Assumes format, type, etc. have already been validated.
2929 DataFormat sourceDataFormat = getDataFormat(format, type); 2938 DataFormat sourceDataFormat = getDataFormat(format, type);
2930 2939
2931 // Resize the output buffer. 2940 // Resize the output buffer.
2932 unsigned int componentsPerPixel, bytesPerComponent; 2941 unsigned int componentsPerPixel, bytesPerComponent;
2933 if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, 2942 if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel,
2934 &bytesPerComponent)) 2943 &bytesPerComponent))
2935 return false; 2944 return false;
2936 unsigned bytesPerPixel = componentsPerPixel * bytesPerComponent; 2945 unsigned bytesPerPixel = componentsPerPixel * bytesPerComponent;
2937 data.resize(width * height * bytesPerPixel); 2946 data.resize(width * height * bytesPerPixel);
2938 2947
2939 if (!packPixels(static_cast<const uint8_t*>(pixels), sourceDataFormat, width, 2948 if (!packPixels(static_cast<const uint8_t*>(pixels), sourceDataFormat, width,
2940 height, unpackAlignment, format, type, 2949 height, IntRect(0, 0, width, height), unpackAlignment, format,
2950 type,
2941 (premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing), 2951 (premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing),
2942 data.data(), flipY)) 2952 data.data(), flipY))
2943 return false; 2953 return false;
2944 2954
2945 return true; 2955 return true;
2946 } 2956 }
2947 2957
2948 bool WebGLImageConversion::packPixels(const uint8_t* sourceData, 2958 bool WebGLImageConversion::packPixels(const uint8_t* sourceData,
2949 DataFormat sourceDataFormat, 2959 DataFormat sourceDataFormat,
2950 unsigned width, 2960 unsigned sourceDataWidth,
2951 unsigned height, 2961 unsigned sourceDataHeight,
2962 const IntRect& sourceDataSubRectangle,
2952 unsigned sourceUnpackAlignment, 2963 unsigned sourceUnpackAlignment,
2953 unsigned destinationFormat, 2964 unsigned destinationFormat,
2954 unsigned destinationType, 2965 unsigned destinationType,
2955 AlphaOp alphaOp, 2966 AlphaOp alphaOp,
2956 void* destinationData, 2967 void* destinationData,
2957 bool flipY) { 2968 bool flipY) {
2958 int validSrc = width * TexelBytesForFormat(sourceDataFormat); 2969 int validSrc = sourceDataWidth * TexelBytesForFormat(sourceDataFormat);
2959 int remainder = 2970 int remainder =
2960 sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0; 2971 sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0;
2961 int srcStride = 2972 int srcStride =
2962 remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc; 2973 remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc;
2974 int srcRowOffset =
2975 sourceDataSubRectangle.x() * TexelBytesForFormat(sourceDataFormat);
2963 2976
2964 DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType); 2977 DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType);
2965 int dstStride = width * TexelBytesForFormat(dstDataFormat); 2978 int dstStride =
2979 sourceDataSubRectangle.width() * TexelBytesForFormat(dstDataFormat);
2966 if (flipY) { 2980 if (flipY) {
2967 destinationData = 2981 destinationData = static_cast<uint8_t*>(destinationData) +
2968 static_cast<uint8_t*>(destinationData) + dstStride * (height - 1); 2982 dstStride * (sourceDataSubRectangle.height() - 1);
2969 dstStride = -dstStride; 2983 dstStride = -dstStride;
2970 } 2984 }
2971 if (!HasAlpha(sourceDataFormat) || !HasColor(sourceDataFormat) || 2985 if (!HasAlpha(sourceDataFormat) || !HasColor(sourceDataFormat) ||
2972 !HasColor(dstDataFormat)) 2986 !HasColor(dstDataFormat))
2973 alphaOp = AlphaDoNothing; 2987 alphaOp = AlphaDoNothing;
2974 2988
2975 if (sourceDataFormat == dstDataFormat && alphaOp == AlphaDoNothing) { 2989 if (sourceDataFormat == dstDataFormat && alphaOp == AlphaDoNothing) {
2976 const uint8_t* ptr = sourceData; 2990 const uint8_t* ptr = sourceData + srcStride * sourceDataSubRectangle.y();
2977 const uint8_t* ptrEnd = sourceData + srcStride * height; 2991 const uint8_t* ptrEnd =
2992 sourceData + srcStride * sourceDataSubRectangle.maxY();
2978 unsigned rowSize = (dstStride > 0) ? dstStride : -dstStride; 2993 unsigned rowSize = (dstStride > 0) ? dstStride : -dstStride;
2979 uint8_t* dst = static_cast<uint8_t*>(destinationData); 2994 uint8_t* dst = static_cast<uint8_t*>(destinationData);
2980 while (ptr < ptrEnd) { 2995 while (ptr < ptrEnd) {
2981 memcpy(dst, ptr, rowSize); 2996 memcpy(dst, ptr + srcRowOffset, rowSize);
2982 ptr += srcStride; 2997 ptr += srcStride;
2983 dst += dstStride; 2998 dst += dstStride;
2984 } 2999 }
2985 return true; 3000 return true;
2986 } 3001 }
2987 3002
2988 FormatConverter converter(width, height, sourceData, destinationData, 3003 FormatConverter converter(sourceDataSubRectangle, sourceData, destinationData,
2989 srcStride, dstStride); 3004 srcStride, srcRowOffset, dstStride);
2990 converter.convert(sourceDataFormat, dstDataFormat, alphaOp); 3005 converter.convert(sourceDataFormat, dstDataFormat, alphaOp);
2991 if (!converter.Success()) 3006 if (!converter.Success())
2992 return false; 3007 return false;
2993 return true; 3008 return true;
2994 } 3009 }
2995 3010
2996 void WebGLImageConversion::unpackPixels(const uint16_t* sourceData, 3011 void WebGLImageConversion::unpackPixels(const uint16_t* sourceData,
2997 DataFormat sourceDataFormat, 3012 DataFormat sourceDataFormat,
2998 unsigned pixelsPerRow, 3013 unsigned pixelsPerRow,
2999 uint8_t* destinationData) { 3014 uint8_t* destinationData) {
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
3089 pack<WebGLImageConversion::DataFormatRGB565, 3104 pack<WebGLImageConversion::DataFormatRGB565,
3090 WebGLImageConversion::AlphaDoNothing>(srcRowStart, dstRowStart, 3105 WebGLImageConversion::AlphaDoNothing>(srcRowStart, dstRowStart,
3091 pixelsPerRow); 3106 pixelsPerRow);
3092 } break; 3107 } break;
3093 default: 3108 default:
3094 break; 3109 break;
3095 } 3110 }
3096 } 3111 }
3097 3112
3098 } // namespace blink 3113 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698