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

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

Issue 2787053004: Respect colorSpace in DecodingImageGenerator::onGetPixels() (Closed)
Patch Set: Response to comments Created 3 years, 8 months 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 /* 1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 18 matching lines...) Expand all
29 #include "platform/graphics/ImageDecodingStore.h" 29 #include "platform/graphics/ImageDecodingStore.h"
30 #include "platform/image-decoders/ImageDecoder.h" 30 #include "platform/image-decoders/ImageDecoder.h"
31 #include "platform/instrumentation/tracing/TraceEvent.h" 31 #include "platform/instrumentation/tracing/TraceEvent.h"
32 #include "third_party/skia/include/core/SkYUVSizeInfo.h" 32 #include "third_party/skia/include/core/SkYUVSizeInfo.h"
33 #include "wtf/PtrUtil.h" 33 #include "wtf/PtrUtil.h"
34 #include <memory> 34 #include <memory>
35 35
36 namespace blink { 36 namespace blink {
37 37
38 static bool compatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) { 38 static bool compatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) {
39 if (src == dst) 39 bool compatibleSize = src.dimensions() == dst.dimensions();
40 return true; 40 bool compatibleColorType = src.colorType() == dst.colorType();
41 41
42 // It is legal to write kOpaque_SkAlphaType pixels into a kPremul_SkAlphaType 42 // We should be lenient with alphaType. Any image can be decoded to kUnpremul
43 // buffer. This can happen when DeferredImageDecoder allocates an 43 // or kPremul. Opaque images may be marked as kOpaque, but not until they
44 // kOpaque_SkAlphaType image generator based on cached frame info, while the 44 // are fully decoded. This means that the buffer may be kPremul, but we are
45 // ImageFrame-allocated dest bitmap stays kPremul_SkAlphaType. 45 // writing kOpaque pixels.
46 if (src.alphaType() == kOpaque_SkAlphaType &&
47 dst.alphaType() == kPremul_SkAlphaType) {
48 const SkImageInfo& tmp = src.makeAlphaType(kPremul_SkAlphaType);
49 return tmp == dst;
50 }
51 46
52 return false; 47 // The color spaces also do not need to match. We will perform color space
48 // conversions when necessary.
49
50 return compatibleSize && compatibleColorType;
53 } 51 }
54 52
55 // Creates a SkPixelRef such that the memory for pixels is given by an external 53 // Creates a SkPixelRef such that the memory for pixels is given by an external
56 // body. This is used to write directly to the memory given by Skia during 54 // body. This is used to write directly to the memory given by Skia during
57 // decoding. 55 // decoding.
58 class ExternalMemoryAllocator final : public SkBitmap::Allocator { 56 class ExternalMemoryAllocator final : public SkBitmap::Allocator {
59 USING_FAST_MALLOC(ExternalMemoryAllocator); 57 USING_FAST_MALLOC(ExternalMemoryAllocator);
60 WTF_MAKE_NONCOPYABLE(ExternalMemoryAllocator); 58 WTF_MAKE_NONCOPYABLE(ExternalMemoryAllocator);
61 59
62 public: 60 public:
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
96 componentWidthBytes[0] = decoder->decodedYUVWidthBytes(0); 94 componentWidthBytes[0] = decoder->decodedYUVWidthBytes(0);
97 size = decoder->decodedYUVSize(1); 95 size = decoder->decodedYUVSize(1);
98 componentSizes[1].set(size.width(), size.height()); 96 componentSizes[1].set(size.width(), size.height());
99 componentWidthBytes[1] = decoder->decodedYUVWidthBytes(1); 97 componentWidthBytes[1] = decoder->decodedYUVWidthBytes(1);
100 size = decoder->decodedYUVSize(2); 98 size = decoder->decodedYUVSize(2);
101 componentSizes[2].set(size.width(), size.height()); 99 componentSizes[2].set(size.width(), size.height());
102 componentWidthBytes[2] = decoder->decodedYUVWidthBytes(2); 100 componentWidthBytes[2] = decoder->decodedYUVWidthBytes(2);
103 return true; 101 return true;
104 } 102 }
105 103
106 ImageFrameGenerator::ImageFrameGenerator(const SkISize& fullSize, 104 ImageFrameGenerator::ImageFrameGenerator(const SkImageInfo& info,
107 bool isMultiFrame, 105 bool isMultiFrame,
108 const ColorBehavior& colorBehavior) 106 const ColorBehavior& colorBehavior)
109 : m_fullSize(fullSize), 107 : m_info(info),
110 m_decoderColorBehavior(colorBehavior), 108 m_decoderColorBehavior(colorBehavior),
111 m_isMultiFrame(isMultiFrame), 109 m_isMultiFrame(isMultiFrame),
112 m_decodeFailed(false), 110 m_decodeFailed(false),
113 m_yuvDecodingFailed(false), 111 m_yuvDecodingFailed(false),
114 m_frameCount(0) {} 112 m_frameCount(0) {}
115 113
116 ImageFrameGenerator::~ImageFrameGenerator() { 114 ImageFrameGenerator::~ImageFrameGenerator() {
117 ImageDecodingStore::instance().removeCacheIndexedByGenerator(this); 115 ImageDecodingStore::instance().removeCacheIndexedByGenerator(this);
118 } 116 }
119 117
120 bool ImageFrameGenerator::decodeAndScale(SegmentReader* data, 118 bool ImageFrameGenerator::decodeAndScale(SegmentReader* data,
121 bool allDataReceived, 119 bool allDataReceived,
122 size_t index, 120 size_t index,
123 const SkImageInfo& info, 121 const SkImageInfo& dstInfo,
124 void* pixels, 122 void* pixels,
125 size_t rowBytes) { 123 size_t rowBytes) {
126 if (m_decodeFailed) 124 if (m_decodeFailed)
127 return false; 125 return false;
128 126
129 TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index", 127 TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index",
130 static_cast<int>(index)); 128 static_cast<int>(index));
131 129
132 // This implementation does not support scaling so check the requested size. 130 // This implementation does not support scaling so check the requested size.
133 SkISize scaledSize = SkISize::Make(info.width(), info.height()); 131 SkISize scaledSize = SkISize::Make(dstInfo.width(), dstInfo.height());
134 ASSERT(m_fullSize == scaledSize); 132 DCHECK(m_info.dimensions() == scaledSize);
scroggo_chromium 2017/04/07 18:06:06 scaledSize looks to be never used again (or it doe
135 133
136 // It is okay to allocate ref-counted ExternalMemoryAllocator on the stack, 134 // It is okay to allocate ref-counted ExternalMemoryAllocator on the stack,
137 // because 1) it contains references to memory that will be invalid after 135 // because 1) it contains references to memory that will be invalid after
138 // returning (i.e. a pointer to |pixels|) and therefore 2) should not live 136 // returning (i.e. a pointer to |pixels|) and therefore 2) should not live
139 // longer than the call to the current method. 137 // longer than the call to the current method.
140 ExternalMemoryAllocator externalAllocator(info, pixels, rowBytes); 138 ExternalMemoryAllocator externalAllocator(dstInfo, pixels, rowBytes);
141 SkBitmap bitmap = tryToResumeDecode(data, allDataReceived, index, scaledSize, 139 SkBitmap bitmap = tryToResumeDecode(data, allDataReceived, index, scaledSize,
142 &externalAllocator); 140 &externalAllocator, dstInfo);
143 DCHECK(externalAllocator.unique()); // Verify we have the only ref-count. 141 DCHECK(externalAllocator.unique()); // Verify we have the only ref-count.
144 142
145 if (bitmap.isNull()) 143 if (bitmap.isNull())
146 return false; 144 return false;
147 145
148 // Check to see if the decoder has written directly to the pixel memory 146 // Check to see if the decoder has written directly to the pixel memory
149 // provided. If not, make a copy. 147 // provided. If not, make a copy.
150 ASSERT(bitmap.width() == scaledSize.width()); 148 ASSERT(bitmap.width() == scaledSize.width());
scroggo_chromium 2017/04/07 18:06:06 If you drop scaledSize, this could be: DCHECK_E
151 ASSERT(bitmap.height() == scaledSize.height()); 149 ASSERT(bitmap.height() == scaledSize.height());
152 SkAutoLockPixels bitmapLock(bitmap); 150 SkAutoLockPixels bitmapLock(bitmap);
153 if (bitmap.getPixels() != pixels) 151 if (bitmap.getPixels() != pixels)
154 return bitmap.copyPixelsTo(pixels, rowBytes * info.height(), rowBytes); 152 return bitmap.readPixels(bitmap.info(), pixels, rowBytes, 0, 0);
scroggo_chromium 2017/04/07 18:06:06 Might bitmap.info() be different from dstInfo? I'm
155 return true; 153 return true;
156 } 154 }
157 155
158 bool ImageFrameGenerator::decodeToYUV(SegmentReader* data, 156 bool ImageFrameGenerator::decodeToYUV(SegmentReader* data,
159 size_t index, 157 size_t index,
160 const SkISize componentSizes[3], 158 const SkISize componentSizes[3],
161 void* planes[3], 159 void* planes[3],
162 const size_t rowBytes[3]) { 160 const size_t rowBytes[3]) {
163 // TODO (scroggo): The only interesting thing this uses from the 161 // TODO (scroggo): The only interesting thing this uses from the
164 // ImageFrameGenerator is m_decodeFailed. Move this into 162 // ImageFrameGenerator is m_decodeFailed. Move this into
(...skipping 24 matching lines...) Expand all
189 if (decoder->decodeToYUV()) { 187 if (decoder->decodeToYUV()) {
190 setHasAlpha(0, false); // YUV is always opaque 188 setHasAlpha(0, false); // YUV is always opaque
191 return true; 189 return true;
192 } 190 }
193 191
194 ASSERT(decoder->failed()); 192 ASSERT(decoder->failed());
195 m_yuvDecodingFailed = true; 193 m_yuvDecodingFailed = true;
196 return false; 194 return false;
197 } 195 }
198 196
199 SkBitmap ImageFrameGenerator::tryToResumeDecode( 197 static inline bool needsColorXform(const SkImageInfo& src,
200 SegmentReader* data, 198 const SkImageInfo& dst) {
201 bool allDataReceived, 199 return src.colorSpace() && dst.colorSpace() &&
202 size_t index, 200 !SkColorSpace::Equals(src.colorSpace(), dst.colorSpace());
203 const SkISize& scaledSize, 201 }
204 SkBitmap::Allocator* allocator) { 202
203 SkBitmap ImageFrameGenerator::tryToResumeDecode(SegmentReader* data,
204 bool allDataReceived,
205 size_t index,
206 const SkISize& scaledSize,
207 SkBitmap::Allocator* allocator,
208 const SkImageInfo& dstInfo) {
205 TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecode", "frame index", 209 TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecode", "frame index",
206 static_cast<int>(index)); 210 static_cast<int>(index));
207 211
208 ImageDecoder* decoder = 0; 212 ImageDecoder* decoder = 0;
209 213
214 const bool needsXform = needsColorXform(m_info, dstInfo);
215 ImageDecoder::AlphaOption alphaOption = ImageDecoder::AlphaPremultiplied;
216 if (needsXform && !dstInfo.isOpaque()) {
scroggo_chromium 2017/04/07 18:06:06 It might be worth noting that you check dstInfo be
msarett1 2017/04/10 14:42:46 Acknowledged.
217 alphaOption = ImageDecoder::AlphaNotPremultiplied;
218 }
219
210 // Lock the mutex, so only one thread can use the decoder at once. 220 // Lock the mutex, so only one thread can use the decoder at once.
211 MutexLocker lock(m_decodeMutex); 221 MutexLocker lock(m_decodeMutex);
212 const bool resumeDecoding = 222 const bool resumeDecoding = ImageDecodingStore::instance().lockDecoder(
213 ImageDecodingStore::instance().lockDecoder(this, m_fullSize, &decoder); 223 this, m_info.dimensions(), alphaOption, &decoder);
214 ASSERT(!resumeDecoding || decoder); 224 ASSERT(!resumeDecoding || decoder);
215 225
216 SkBitmap fullSizeImage; 226 SkBitmap fullSizeImage;
217 bool complete = 227 bool complete = decode(data, allDataReceived, index, &decoder, &fullSizeImage,
218 decode(data, allDataReceived, index, &decoder, &fullSizeImage, allocator); 228 allocator, alphaOption, dstInfo);
219 229
220 if (!decoder) 230 if (!decoder)
221 return SkBitmap(); 231 return SkBitmap();
222 232
223 // If we are not resuming decoding that means the decoder is freshly 233 // If we are not resuming decoding that means the decoder is freshly
224 // created and we have ownership. If we are resuming decoding then 234 // created and we have ownership. If we are resuming decoding then
225 // the decoder is owned by ImageDecodingStore. 235 // the decoder is owned by ImageDecodingStore.
226 std::unique_ptr<ImageDecoder> decoderContainer; 236 std::unique_ptr<ImageDecoder> decoderContainer;
227 if (!resumeDecoding) 237 if (!resumeDecoding)
228 decoderContainer = WTF::wrapUnique(decoder); 238 decoderContainer = WTF::wrapUnique(decoder);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 m_hasAlpha[i] = true; 283 m_hasAlpha[i] = true;
274 } 284 }
275 m_hasAlpha[index] = hasAlpha; 285 m_hasAlpha[index] = hasAlpha;
276 } 286 }
277 287
278 bool ImageFrameGenerator::decode(SegmentReader* data, 288 bool ImageFrameGenerator::decode(SegmentReader* data,
279 bool allDataReceived, 289 bool allDataReceived,
280 size_t index, 290 size_t index,
281 ImageDecoder** decoder, 291 ImageDecoder** decoder,
282 SkBitmap* bitmap, 292 SkBitmap* bitmap,
283 SkBitmap::Allocator* allocator) { 293 SkBitmap::Allocator* allocator,
294 ImageDecoder::AlphaOption alphaOption,
295 const SkImageInfo& dstInfo) {
284 ASSERT(m_decodeMutex.locked()); 296 ASSERT(m_decodeMutex.locked());
285 TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width", 297 TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width", m_info.width(),
286 m_fullSize.width(), "height", m_fullSize.height()); 298 "height", m_info.height());
287 299
288 // Try to create an ImageDecoder if we are not given one. 300 // Try to create an ImageDecoder if we are not given one.
289 ASSERT(decoder); 301 ASSERT(decoder);
290 bool newDecoder = false; 302 bool newDecoder = false;
291 bool shouldCallSetData = true; 303 bool shouldCallSetData = true;
292 if (!*decoder) { 304 if (!*decoder) {
293 newDecoder = true; 305 newDecoder = true;
294 if (m_imageDecoderFactory) 306 if (m_imageDecoderFactory)
295 *decoder = m_imageDecoderFactory->create().release(); 307 *decoder = m_imageDecoderFactory->create().release();
296 308
297 if (!*decoder) { 309 if (!*decoder) {
298 *decoder = ImageDecoder::create(data, allDataReceived, 310 *decoder = ImageDecoder::create(data, allDataReceived, alphaOption,
299 ImageDecoder::AlphaPremultiplied,
300 m_decoderColorBehavior) 311 m_decoderColorBehavior)
301 .release(); 312 .release();
302 // The newly created decoder just grabbed the data. No need to reset it. 313 // The newly created decoder just grabbed the data. No need to reset it.
303 shouldCallSetData = false; 314 shouldCallSetData = false;
304 } 315 }
305 316
306 if (!*decoder) 317 if (!*decoder)
307 return false; 318 return false;
308 } 319 }
309 320
310
311 if (shouldCallSetData) 321 if (shouldCallSetData)
312 (*decoder)->setData(data, allDataReceived); 322 (*decoder)->setData(data, allDataReceived);
313 323
314 bool usingExternalAllocator = false; 324 bool usingExternalAllocator = false;
315 325
316 // For multi-frame image decoders, we need to know how many frames are 326 // For multi-frame image decoders, we need to know how many frames are
317 // in that image in order to release the decoder when all frames are 327 // in that image in order to release the decoder when all frames are
318 // decoded. frameCount() is reliable only if all data is received and set in 328 // decoded. frameCount() is reliable only if all data is received and set in
319 // decoder, particularly with GIF. 329 // decoder, particularly with GIF.
320 if (allDataReceived) { 330 if (allDataReceived) {
(...skipping 22 matching lines...) Expand all
343 return false; 353 return false;
344 354
345 // A cache object is considered complete if we can decode a complete frame. 355 // A cache object is considered complete if we can decode a complete frame.
346 // Or we have received all data. The image might not be fully decoded in 356 // Or we have received all data. The image might not be fully decoded in
347 // the latter case. 357 // the latter case.
348 const bool isDecodeComplete = 358 const bool isDecodeComplete =
349 frame->getStatus() == ImageFrame::FrameComplete || allDataReceived; 359 frame->getStatus() == ImageFrame::FrameComplete || allDataReceived;
350 360
351 SkBitmap fullSizeBitmap = frame->bitmap(); 361 SkBitmap fullSizeBitmap = frame->bitmap();
352 if (!fullSizeBitmap.isNull()) { 362 if (!fullSizeBitmap.isNull()) {
353 ASSERT(fullSizeBitmap.width() == m_fullSize.width() && 363 DCHECK(fullSizeBitmap.width() == m_info.width() &&
scroggo_chromium 2017/04/07 18:06:06 So long as you're changing this, it could be: D
msarett1 2017/04/10 14:42:46 Acknowledged.
354 fullSizeBitmap.height() == m_fullSize.height()); 364 fullSizeBitmap.height() == m_info.height());
355 setHasAlpha(index, !fullSizeBitmap.isOpaque()); 365 setHasAlpha(index, !fullSizeBitmap.isOpaque());
356 } 366 }
357 367
368 if (needsColorXform(m_info, dstInfo)) {
scroggo_chromium 2017/04/07 18:06:06 I assume you do not need to do this if fullSizeBit
msarett1 2017/04/10 14:42:46 Acknowledged.
369 DCHECK(dstInfo.isOpaque() ||
370 ImageDecoder::AlphaNotPremultiplied == alphaOption);
371 std::unique_ptr<SkColorSpaceXform> xform =
372 SkColorSpaceXform::New(m_info.colorSpace(), dstInfo.colorSpace());
373
374 SkBitmap tmp(fullSizeBitmap);
scroggo_chromium 2017/04/07 18:06:06 IIRC, this will make tmp share fullSizeBitmap's Sk
msarett1 2017/04/10 14:42:46 Acknowledged.
375 tmp.lockPixels();
376 uint32_t* row = tmp.getAddr32(0, 0);
377 for (int y = 0; y < dstInfo.height(); y++) {
scroggo_chromium 2017/04/07 18:06:06 This method is called while m_decodeMutex is locke
msarett1 2017/04/10 14:42:45 Acknowledged.
378 SkColorSpaceXform::ColorFormat format =
379 SkColorSpaceXform::kRGBA_8888_ColorFormat;
380 if (kN32_SkColorType == kBGRA_8888_SkColorType) {
381 format = SkColorSpaceXform::kBGRA_8888_ColorFormat;
382 }
383 SkAlphaType alphaType =
384 dstInfo.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
scroggo_chromium 2017/04/07 18:06:06 My first thought was that you could use dstInfo.al
msarett1 2017/04/10 14:42:46 Acknowledged.
385 bool xformed =
386 xform->apply(format, row, format, row, dstInfo.width(), alphaType);
387 DCHECK(xformed);
388
389 // To be compatible with dst space blending, premultiply in the dst space.
390 if (kPremul_SkAlphaType == dstInfo.alphaType()) {
391 for (int x = 0; x < dstInfo.width(); x++) {
392 row[x] =
393 SkPreMultiplyARGB(SkGetPackedA32(row[x]), SkGetPackedR32(row[x]),
394 SkGetPackedG32(row[x]), SkGetPackedB32(row[x]));
395 }
396 }
397
398 row = (uint32_t*)(((uint8_t*)row) + fullSizeBitmap.rowBytes());
scroggo_chromium 2017/04/07 18:06:06 I think tmp and fullSizeBitmap are essentially the
msarett1 2017/04/10 14:42:46 Acknowledged.
399 }
400 }
401
358 *bitmap = fullSizeBitmap; 402 *bitmap = fullSizeBitmap;
359 return isDecodeComplete; 403 return isDecodeComplete;
360 } 404 }
361 405
362 bool ImageFrameGenerator::hasAlpha(size_t index) { 406 bool ImageFrameGenerator::hasAlpha(size_t index) {
363 MutexLocker lock(m_alphaMutex); 407 MutexLocker lock(m_alphaMutex);
364 if (index < m_hasAlpha.size()) 408 if (index < m_hasAlpha.size())
365 return m_hasAlpha[index]; 409 return m_hasAlpha[index];
366 return true; 410 return true;
367 } 411 }
368 412
369 bool ImageFrameGenerator::getYUVComponentSizes(SegmentReader* data, 413 bool ImageFrameGenerator::getYUVComponentSizes(SegmentReader* data,
370 SkYUVSizeInfo* sizeInfo) { 414 SkYUVSizeInfo* sizeInfo) {
371 TRACE_EVENT2("blink", "ImageFrameGenerator::getYUVComponentSizes", "width", 415 TRACE_EVENT2("blink", "ImageFrameGenerator::getYUVComponentSizes", "width",
372 m_fullSize.width(), "height", m_fullSize.height()); 416 m_info.width(), "height", m_info.height());
373 417
374 if (m_yuvDecodingFailed) 418 if (m_yuvDecodingFailed)
375 return false; 419 return false;
376 420
377 std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create( 421 std::unique_ptr<ImageDecoder> decoder = ImageDecoder::create(
378 data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorBehavior); 422 data, true, ImageDecoder::AlphaPremultiplied, m_decoderColorBehavior);
379 if (!decoder) 423 if (!decoder)
380 return false; 424 return false;
381 425
382 // Setting a dummy ImagePlanes object signals to the decoder that we want to 426 // Setting a dummy ImagePlanes object signals to the decoder that we want to
383 // do YUV decoding. 427 // do YUV decoding.
384 std::unique_ptr<ImagePlanes> dummyImagePlanes = 428 std::unique_ptr<ImagePlanes> dummyImagePlanes =
385 WTF::wrapUnique(new ImagePlanes); 429 WTF::wrapUnique(new ImagePlanes);
386 decoder->setImagePlanes(std::move(dummyImagePlanes)); 430 decoder->setImagePlanes(std::move(dummyImagePlanes));
387 431
388 return updateYUVComponentSizes(decoder.get(), sizeInfo->fSizes, 432 return updateYUVComponentSizes(decoder.get(), sizeInfo->fSizes,
389 sizeInfo->fWidthBytes); 433 sizeInfo->fWidthBytes);
390 } 434 }
391 435
392 } // namespace blink 436 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698