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

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

Issue 141483004: Optimization for image decoding using Skia discardable memory (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: done Created 6 years, 10 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 | Annotate | Revision Log
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 17 matching lines...) Expand all
28 #include "platform/graphics/ImageFrameGenerator.h" 28 #include "platform/graphics/ImageFrameGenerator.h"
29 29
30 #include "platform/SharedBuffer.h" 30 #include "platform/SharedBuffer.h"
31 #include "platform/TraceEvent.h" 31 #include "platform/TraceEvent.h"
32 #include "platform/graphics/DiscardablePixelRef.h" 32 #include "platform/graphics/DiscardablePixelRef.h"
33 #include "platform/graphics/ImageDecodingStore.h" 33 #include "platform/graphics/ImageDecodingStore.h"
34 #include "platform/graphics/ScaledImageFragment.h" 34 #include "platform/graphics/ScaledImageFragment.h"
35 #include "platform/image-decoders/ImageDecoder.h" 35 #include "platform/image-decoders/ImageDecoder.h"
36 36
37 #include "skia/ext/image_operations.h" 37 #include "skia/ext/image_operations.h"
38 #include "third_party/skia/include/core/SkMallocPixelRef.h"
38 39
39 namespace WebCore { 40 namespace WebCore {
40 41
42 // Creates a SkPixelRef such that the memory for pixels is given by an external body.
43 // This is used to write directly to the memory given by Skia during decoding.
44 class ImageFrameGenerator::ExternalMemoryAllocator : public SkBitmap::Allocator {
45 public:
46 ExternalMemoryAllocator(const SkImageInfo& info, void* pixels, size_t rowByt es)
47 : m_info(info)
48 , m_pixels(pixels)
49 , m_rowBytes(rowBytes)
50 {
51 }
52
53 virtual bool allocPixelRef(SkBitmap* dst, SkColorTable* ctable) OVERRIDE
54 {
55 SkImageInfo info;
56 if (!dst->asImageInfo(&info))
57 return false;
58
59 if (info != m_info || m_rowBytes != dst->rowBytes())
60 return false;
61
62 if (!dst->installPixels(m_info, m_pixels, m_rowBytes, 0, 0))
63 return false;
64 dst->lockPixels();
65 return true;
66 }
67
68 private:
69 SkImageInfo m_info;
70 void* m_pixels;
71 size_t m_rowBytes;
72 };
73
41 ImageFrameGenerator::ImageFrameGenerator(const SkISize& fullSize, PassRefPtr<Sha redBuffer> data, bool allDataReceived, bool isMultiFrame) 74 ImageFrameGenerator::ImageFrameGenerator(const SkISize& fullSize, PassRefPtr<Sha redBuffer> data, bool allDataReceived, bool isMultiFrame)
42 : m_fullSize(fullSize) 75 : m_fullSize(fullSize)
43 , m_isMultiFrame(isMultiFrame) 76 , m_isMultiFrame(isMultiFrame)
44 , m_decodeFailedAndEmpty(false) 77 , m_decodeFailedAndEmpty(false)
45 , m_decodeCount(ScaledImageFragment::FirstPartialImage) 78 , m_decodeCount(ScaledImageFragment::FirstPartialImage)
46 , m_allocator(adoptPtr(new DiscardablePixelRefAllocator())) 79 , m_discardableAllocator(adoptPtr(new DiscardablePixelRefAllocator()))
47 { 80 {
48 setData(data.get(), allDataReceived); 81 setData(data.get(), allDataReceived);
49 } 82 }
50 83
51 ImageFrameGenerator::~ImageFrameGenerator() 84 ImageFrameGenerator::~ImageFrameGenerator()
52 { 85 {
53 // FIXME: This check is not really thread-safe. This should be changed to: 86 // FIXME: This check is not really thread-safe. This should be changed to:
54 // ImageDecodingStore::removeCacheFromInstance(this); 87 // ImageDecodingStore::removeCacheFromInstance(this);
55 // Which uses a lock internally. 88 // Which uses a lock internally.
56 if (ImageDecodingStore::instance()) 89 if (ImageDecodingStore::instance())
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 121
89 cachedImage = tryToResumeDecode(scaledSize, index); 122 cachedImage = tryToResumeDecode(scaledSize, index);
90 if (cachedImage) 123 if (cachedImage)
91 return cachedImage; 124 return cachedImage;
92 return 0; 125 return 0;
93 } 126 }
94 127
95 bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, void* pixels, size_t rowBytes) 128 bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, void* pixels, size_t rowBytes)
96 { 129 {
97 // This method is called to populate a discardable memory owned by Skia. 130 // This method is called to populate a discardable memory owned by Skia.
98 // Ideally we want the decoder to write directly to |pixels| but this 131
99 // simple implementation copies from a decoded bitmap. 132 // Prevents concurrent decode or scale operations on the same image data.
133 MutexLocker lock(m_decodeMutex);
100 134
101 // This implementation does not support scaling so check the requested size. 135 // This implementation does not support scaling so check the requested size.
102 ASSERT(m_fullSize.width() == info.fWidth); 136 SkISize scaledSize = SkISize::Make(info.fWidth, info.fHeight);
103 ASSERT(m_fullSize.height() == info.fHeight); 137 ASSERT(m_fullSize == scaledSize);
138
139 if (m_decodeFailedAndEmpty)
140 return 0;
141
142 TRACE_EVENT2("webkit", "ImageFrameGenerator::decodeAndScale", "generator", t his, "decodeCount", static_cast<int>(m_decodeCount));
104 143
105 // Don't use discardable memory for decoding if Skia is providing output 144 // Don't use discardable memory for decoding if Skia is providing output
106 // memory. By clearing the memory allocator decoding will use heap memory. 145 // memory. Instead use ExternalMemoryAllocator such that we can
146 // write directly to the memory given by Skia.
107 // 147 //
108 // TODO: 148 // TODO:
109 // This is not pretty because this class is used in two different code 149 // This is not pretty because this class is used in two different code
110 // paths: discardable memory decoding on Android and discardable memory 150 // paths: discardable memory decoding on Android and discardable memory
111 // in Skia. Once the transition to caching in Skia is complete we can get 151 // in Skia. Once the transition to caching in Skia is complete we can get
112 // rid of the logic that handles discardable memory. 152 // rid of the logic that handles discardable memory.
113 m_allocator.clear(); 153 m_discardableAllocator.clear();
154 m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, row Bytes));
114 155
115 const ScaledImageFragment* cachedImage = decodeAndScale(SkISize::Make(info.f Width, info.fHeight), index); 156 const ScaledImageFragment* cachedImage = tryToResumeDecode(scaledSize, index );
116 if (!cachedImage) 157 if (!cachedImage)
117 return false; 158 return false;
118 159
119 ASSERT(cachedImage->bitmap().width() == info.fWidth); 160 // Don't keep the allocator because it contains a pointer to memory
120 ASSERT(cachedImage->bitmap().height() == info.fHeight); 161 // that we do not own.
162 m_externalAllocator.clear();
121 163
122 bool copied = cachedImage->bitmap().copyPixelsTo(pixels, rowBytes * info.fHe ight, rowBytes); 164 ASSERT(cachedImage->bitmap().width() == scaledSize.width());
165 ASSERT(cachedImage->bitmap().height() == scaledSize.height());
166
167 // If the image is fully decoded and is not multi-frame then we have
168 // written directly to the memory given by Skia. There is no need to
169 // copy.
170 bool result = true;
171 if (!cachedImage->isComplete() || m_isMultiFrame)
172 result = cachedImage->bitmap().copyPixelsTo(pixels, rowBytes * info.fHei ght, rowBytes);
123 ImageDecodingStore::instance()->unlockCache(this, cachedImage); 173 ImageDecodingStore::instance()->unlockCache(this, cachedImage);
124 return copied; 174 return result;
125 } 175 }
126 176
127 const ScaledImageFragment* ImageFrameGenerator::tryToLockCompleteCache(const SkI Size& scaledSize, size_t index) 177 const ScaledImageFragment* ImageFrameGenerator::tryToLockCompleteCache(const SkI Size& scaledSize, size_t index)
128 { 178 {
129 const ScaledImageFragment* cachedImage = 0; 179 const ScaledImageFragment* cachedImage = 0;
130 if (ImageDecodingStore::instance()->lockCache(this, scaledSize, index, &cach edImage)) 180 if (ImageDecodingStore::instance()->lockCache(this, scaledSize, index, &cach edImage))
131 return cachedImage; 181 return cachedImage;
132 return 0; 182 return 0;
133 } 183 }
134 184
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 return cachedImage; 231 return cachedImage;
182 } 232 }
183 233
184 PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD ecoder** decoder) 234 PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD ecoder** decoder)
185 { 235 {
186 TRACE_EVENT2("webkit", "ImageFrameGenerator::decode", "width", m_fullSize.wi dth(), "height", m_fullSize.height()); 236 TRACE_EVENT2("webkit", "ImageFrameGenerator::decode", "width", m_fullSize.wi dth(), "height", m_fullSize.height());
187 237
188 ASSERT(decoder); 238 ASSERT(decoder);
189 SharedBuffer* data = 0; 239 SharedBuffer* data = 0;
190 bool allDataReceived = false; 240 bool allDataReceived = false;
241 bool newDecoder = false;
191 m_data.data(&data, &allDataReceived); 242 m_data.data(&data, &allDataReceived);
192 243
193 // Try to create an ImageDecoder if we are not given one. 244 // Try to create an ImageDecoder if we are not given one.
194 if (!*decoder) { 245 if (!*decoder) {
246 newDecoder = true;
195 if (m_imageDecoderFactory) 247 if (m_imageDecoderFactory)
196 *decoder = m_imageDecoderFactory->create().leakPtr(); 248 *decoder = m_imageDecoderFactory->create().leakPtr();
197 249
198 if (!*decoder) 250 if (!*decoder)
199 *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultipli ed, ImageSource::GammaAndColorProfileApplied).leakPtr(); 251 *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultipli ed, ImageSource::GammaAndColorProfileApplied).leakPtr();
200 252
201 if (!*decoder) 253 if (!*decoder)
202 return nullptr; 254 return nullptr;
203 } 255 }
204 256
205 // TODO: this is very ugly. We need to refactor the way how we can pass a 257 if (!m_isMultiFrame && newDecoder && allDataReceived) {
206 // memory allocator to image decoders. 258 // We are supporting two decoding paths in this code. Use the
207 if (!m_isMultiFrame) 259 // external memory allocator in the Skia discardable path to
208 (*decoder)->setMemoryAllocator(m_allocator.get()); 260 // save one memory copy.
261 if (m_externalAllocator)
262 (*decoder)->setMemoryAllocator(m_externalAllocator.get());
263 else
264 (*decoder)->setMemoryAllocator(m_discardableAllocator.get());
265 }
209 (*decoder)->setData(data, allDataReceived); 266 (*decoder)->setData(data, allDataReceived);
210 // If this call returns a newly allocated DiscardablePixelRef, then 267 // If this call returns a newly allocated DiscardablePixelRef, then
211 // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked. 268 // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked.
212 // They will be unlocked when ImageDecoder is destroyed since ImageDecoder 269 // They will be unlocked when ImageDecoder is destroyed since ImageDecoder
213 // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the 270 // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the
214 // ImageDecodingStore while locked. 271 // ImageDecodingStore while locked.
215 ImageFrame* frame = (*decoder)->frameBufferAtIndex(index); 272 ImageFrame* frame = (*decoder)->frameBufferAtIndex(index);
216 (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder. 273 (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.
217 (*decoder)->clearCacheExceptFrame(index); 274 (*decoder)->clearCacheExceptFrame(index);
275 (*decoder)->setMemoryAllocator(0);
218 276
219 if (!frame || frame->status() == ImageFrame::FrameEmpty) 277 if (!frame || frame->status() == ImageFrame::FrameEmpty)
220 return nullptr; 278 return nullptr;
221 279
222 const bool isComplete = frame->status() == ImageFrame::FrameComplete; 280 // A cache object is considered complete if we can decode a complete frame.
281 // Or we have received all data. The image might not be fully decoded in
282 // the latter case.
283 const bool isCacheComplete = frame->status() == ImageFrame::FrameComplete || allDataReceived;
223 SkBitmap fullSizeBitmap = frame->getSkBitmap(); 284 SkBitmap fullSizeBitmap = frame->getSkBitmap();
224 if (fullSizeBitmap.isNull()) 285 if (fullSizeBitmap.isNull())
225 return nullptr; 286 return nullptr;
226 287
227 { 288 {
228 MutexLocker lock(m_alphaMutex); 289 MutexLocker lock(m_alphaMutex);
229 if (index >= m_hasAlpha.size()) { 290 if (index >= m_hasAlpha.size()) {
230 const size_t oldSize = m_hasAlpha.size(); 291 const size_t oldSize = m_hasAlpha.size();
231 m_hasAlpha.resize(index + 1); 292 m_hasAlpha.resize(index + 1);
232 for (size_t i = oldSize; i < m_hasAlpha.size(); ++i) 293 for (size_t i = oldSize; i < m_hasAlpha.size(); ++i)
233 m_hasAlpha[i] = true; 294 m_hasAlpha[i] = true;
234 } 295 }
235 m_hasAlpha[index] = !fullSizeBitmap.isOpaque(); 296 m_hasAlpha[index] = !fullSizeBitmap.isOpaque();
236 } 297 }
237 ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height () == m_fullSize.height()); 298 ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height () == m_fullSize.height());
238 299
239 if (isComplete) 300 if (isCacheComplete)
240 return ScaledImageFragment::createComplete(m_fullSize, index, fullSizeBi tmap); 301 return ScaledImageFragment::createComplete(m_fullSize, index, fullSizeBi tmap);
241 302
242 // If the image is partial we need to return a copy. This is to avoid future 303 // If the image is partial we need to return a copy. This is to avoid future
243 // decode operations writing to the same bitmap. 304 // decode operations writing to the same bitmap.
305 // FIXME: Note that discardable allocator is used. This is because the code
306 // is still used in the Android discardable memory path. When this code is
307 // used in the Skia discardable memory path |m_discardableAllocator| is empt y.
308 // This is confusing and should be cleaned up when we can deprecate the use
309 // case for Android discardable memory.
244 SkBitmap copyBitmap; 310 SkBitmap copyBitmap;
245 return fullSizeBitmap.copyTo(&copyBitmap, fullSizeBitmap.config(), m_allocat or.get()) ? 311 return fullSizeBitmap.copyTo(&copyBitmap, fullSizeBitmap.config(), m_discard ableAllocator.get()) ?
246 ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId() , copyBitmap) : nullptr; 312 ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId() , copyBitmap) : nullptr;
247 } 313 }
248 314
249 bool ImageFrameGenerator::hasAlpha(size_t index) 315 bool ImageFrameGenerator::hasAlpha(size_t index)
250 { 316 {
251 MutexLocker lock(m_alphaMutex); 317 MutexLocker lock(m_alphaMutex);
252 if (index < m_hasAlpha.size()) 318 if (index < m_hasAlpha.size())
253 return m_hasAlpha[index]; 319 return m_hasAlpha[index];
254 return true; 320 return true;
255 } 321 }
256 322
257 } // namespace 323 } // namespace
OLDNEW
« no previous file with comments | « Source/platform/graphics/ImageFrameGenerator.h ('k') | Source/platform/graphics/ImageFrameGeneratorTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698