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

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