1 /*
2 * Copyright (c) 2008, Google Inc. All rights reserved.
3 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "config.h"
34 #include "platform/graphics/ImageBuffer.h"
35
36 #include "GrContext.h"
37 #include "platform/MIMETypeRegistry.h"
38 #include "platform/geometry/IntRect.h"
39 #include "platform/graphics/BitmapImage.h"
40 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/GraphicsTypes3D.h"
42 #include "platform/graphics/ImageBufferClient.h"
43 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
44 #include "platform/graphics/gpu/DrawingBuffer.h"
45 #include "platform/graphics/gpu/Extensions3DUtil.h"
46 #include "platform/graphics/skia/NativeImageSkia.h"
47 #include "platform/graphics/skia/SkiaUtils.h"
48 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
49 #include "platform/image-encoders/skia/PNGImageEncoder.h"
50 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
51 #include "public/platform/Platform.h"
52 #include "public/platform/WebExternalTextureMailbox.h"
53 #include "public/platform/WebGraphicsContext3D.h"
54 #include "public/platform/WebGraphicsContext3DProvider.h"
55 #include "third_party/skia/include/core/SkPicture.h"
56 #include "third_party/skia/include/effects/SkTableColorFilter.h"
57 #include "wtf/MathExtras.h"
58 #include "wtf/Vector.h"
59 #include "wtf/text/Base64.h"
60 #include "wtf/text/WTFString.h"
61
62 namespace blink {
63
create(PassOwnPtr<ImageBufferSurface> surface)64 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
65 {
66 if (!surface->isValid())
67 return nullptr;
68 return adoptPtr(new ImageBuffer(surface));
69 }
70
create(const IntSize & size,OpacityMode opacityMode)71 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
72 {
73 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
74 if (!surface->isValid())
75 return nullptr;
76 return adoptPtr(new ImageBuffer(surface.release()));
77 }
78
ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)79 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
80 : m_surface(surface)
81 , m_client(0)
82 {
83 if (m_surface->canvas()) {
84 m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
85 m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
86 m_context->setAccelerated(m_surface->isAccelerated());
87 }
88 m_surface->setImageBuffer(this);
89 }
90
~ImageBuffer()91 ImageBuffer::~ImageBuffer()
92 {
93 }
94
context() const95 GraphicsContext* ImageBuffer::context() const
96 {
97 if (!isSurfaceValid())
98 return 0;
99 ASSERT(m_context.get());
100 return m_context.get();
101 }
102
bitmap() const103 const SkBitmap& ImageBuffer::bitmap() const
104 {
105 return m_surface->bitmap();
106 }
107
isSurfaceValid() const108 bool ImageBuffer::isSurfaceValid() const
109 {
110 return m_surface->isValid();
111 }
112
isDirty()113 bool ImageBuffer::isDirty()
114 {
115 return m_client ? m_client->isDirty() : false;
116 }
117
didFinalizeFrame()118 void ImageBuffer::didFinalizeFrame()
119 {
120 if (m_client)
121 m_client->didFinalizeFrame();
122 }
123
finalizeFrame(const FloatRect & dirtyRect)124 void ImageBuffer::finalizeFrame(const FloatRect &dirtyRect)
125 {
126 m_surface->finalizeFrame(dirtyRect);
127 didFinalizeFrame();
128 }
129
restoreSurface() const130 bool ImageBuffer::restoreSurface() const
131 {
132 return m_surface->isValid() || m_surface->restore();
133 }
134
notifySurfaceInvalid()135 void ImageBuffer::notifySurfaceInvalid()
136 {
137 if (m_client)
138 m_client->notifySurfaceInvalid();
139 }
140
deepSkBitmapCopy(const SkBitmap & bitmap)141 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
142 {
143 SkBitmap tmp;
144 if (!bitmap.deepCopyTo(&tmp))
145 bitmap.copyTo(&tmp, bitmap.colorType());
146
147 return tmp;
148 }
149
copyImage(BackingStoreCopy copyBehavior,ScaleBehavior) const150 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
151 {
152 if (!isSurfaceValid())
153 return BitmapImage::create(NativeImageSkia::create());
154
155 const SkBitmap& bitmap = m_surface->bitmap();
156 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
157 }
158
fastCopyImageMode()159 BackingStoreCopy ImageBuffer::fastCopyImageMode()
160 {
161 return DontCopyBackingStore;
162 }
163
platformLayer() const164 WebLayer* ImageBuffer::platformLayer() const
165 {
166 return m_surface->layer();
167 }
168
copyToPlatformTexture(WebGraphicsContext3D * context,Platform3DObject texture,GLenum internalFormat,GLenum destType,GLint level,bool premultiplyAlpha,bool flipY)169 bool ImageBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
170 {
171 if (!m_surface->isAccelerated() || !getBackingTexture() || !isSurfaceValid())
172 return false;
173
174 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
175 return false;
176
177 OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
178 if (!provider)
179 return false;
180 WebGraphicsContext3D* sharedContext = provider->context3d();
181 if (!sharedContext)
182 return false;
183
184 OwnPtr<WebExternalTextureMailbox> mailbox = adoptPtr(new WebExternalTextureMailbox);
185
186 // Contexts may be in a different share group. We must transfer the texture through a mailbox first
187 sharedContext->genMailboxCHROMIUM(mailbox->name);
188 sharedContext->produceTextureDirectCHROMIUM(getBackingTexture(), GL_TEXTURE_2D, mailbox->name);
189 sharedContext->flush();
190
191 mailbox->syncPoint = sharedContext->insertSyncPoint();
192
193 context->waitSyncPoint(mailbox->syncPoint);
194 Platform3DObject sourceTexture = context->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox->name);
195
196 // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
197 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
198
199 // The canvas is stored in an inverted position, so the flip semantics are reversed.
200 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, !flipY);
201 context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
202
203 context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
204 context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
205
206 context->deleteTexture(sourceTexture);
207
208 context->flush();
209 sharedContext->waitSyncPoint(context->insertSyncPoint());
210
211 // Undo grContext texture binding changes introduced in this function
212 provider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
213
214 return true;
215 }
216
drawNeedsCopy(GraphicsContext * src,GraphicsContext * dst)217 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
218 {
219 ASSERT(dst);
220 return (src == dst);
221 }
222
getBackingTexture()223 Platform3DObject ImageBuffer::getBackingTexture()
224 {
225 return m_surface->getBackingTexture();
226 }
227
didModifyBackingTexture()228 void ImageBuffer::didModifyBackingTexture()
229 {
230 m_surface->didModifyBackingTexture();
231 }
232
copyRenderingResultsFromDrawingBuffer(DrawingBuffer * drawingBuffer,bool fromFrontBuffer)233 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer, bool fromFrontBuffer)
234 {
235 if (!drawingBuffer)
236 return false;
237 OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
238 if (!provider)
239 return false;
240 WebGraphicsContext3D* context3D = provider->context3d();
241 Platform3DObject tex = m_surface->getBackingTexture();
242 if (!context3D || !tex)
243 return false;
244
245 m_surface->invalidateCachedBitmap();
246 bool result = drawingBuffer->copyToPlatformTexture(context3D, tex, GL_RGBA,
247 GL_UNSIGNED_BYTE, 0, true, false, fromFrontBuffer);
248
249 if (result) {
250 m_surface->didModifyBackingTexture();
251 }
252
253 return result;
254 }
255
draw(GraphicsContext * context,const FloatRect & destRect,const FloatRect * srcPtr,CompositeOperator op,WebBlendMode blendMode)256 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect* srcPtr, CompositeOperator op, WebBlendMode blendMode)
257 {
258 if (!isSurfaceValid())
259 return;
260
261 FloatRect srcRect = srcPtr ? *srcPtr : FloatRect(FloatPoint(), size());
262 RefPtr<SkPicture> picture = m_surface->getPicture();
263 if (picture) {
264 context->drawPicture(picture.release(), destRect, srcRect, op, blendMode);
265 return;
266 }
267
268 SkBitmap bitmap = m_surface->bitmap();
269 // For ImageBufferSurface that enables cachedBitmap, Use the cached Bitmap for CPU side usage
270 // if it is available, otherwise generate and use it.
271 if (!context->isAccelerated() && m_surface->isAccelerated() && m_surface->cachedBitmapEnabled() && isSurfaceValid()) {
272 m_surface->updateCachedBitmapIfNeeded();
273 bitmap = m_surface->cachedBitmap();
274 }
275
276 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
277
278 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation);
279 }
280
flush()281 void ImageBuffer::flush()
282 {
283 if (m_surface->canvas()) {
284 m_surface->canvas()->flush();
285 }
286 }
287
drawPattern(GraphicsContext * context,const FloatRect & srcRect,const FloatSize & scale,const FloatPoint & phase,CompositeOperator op,const FloatRect & destRect,WebBlendMode blendMode,const IntSize & repeatSpacing)288 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
289 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing)
290 {
291 if (!isSurfaceValid())
292 return;
293
294 const SkBitmap& bitmap = m_surface->bitmap();
295 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
296 image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
297 }
298
transformColorSpace(ColorSpace srcColorSpace,ColorSpace dstColorSpace)299 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
300 {
301 const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
302 if (!lookUpTable)
303 return;
304
305 // FIXME: Disable color space conversions on accelerated canvases (for now).
306 if (context()->isAccelerated() || !isSurfaceValid())
307 return;
308
309 const SkBitmap& bitmap = m_surface->bitmap();
310 if (bitmap.isNull())
311 return;
312
313 ASSERT(bitmap.colorType() == kN32_SkColorType);
314 IntSize size = m_surface->size();
315 SkAutoLockPixels bitmapLock(bitmap);
316 for (int y = 0; y < size.height(); ++y) {
317 uint32_t* srcRow = bitmap.getAddr32(0, y);
318 for (int x = 0; x < size.width(); ++x) {
319 SkColor color = SkPMColorToColor(srcRow[x]);
320 srcRow[x] = SkPreMultiplyARGB(
321 SkColorGetA(color),
322 lookUpTable[SkColorGetR(color)],
323 lookUpTable[SkColorGetG(color)],
324 lookUpTable[SkColorGetB(color)]);
325 }
326 }
327 }
328
createColorSpaceFilter(ColorSpace srcColorSpace,ColorSpace dstColorSpace)329 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
330 ColorSpace dstColorSpace)
331 {
332 const uint8_t* lut = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
333 if (!lut)
334 return nullptr;
335
336 return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
337 }
338
getImageData(Multiply multiplied,const IntRect & rect) const339 PassRefPtr<Uint8ClampedArray> ImageBuffer::getImageData(Multiply multiplied, const IntRect& rect) const
340 {
341 if (!isSurfaceValid())
342 return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
343
344 float area = 4.0f * rect.width() * rect.height();
345 if (area > static_cast<float>(std::numeric_limits<int>::max()))
346 return nullptr;
347
348 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
349
350 if (rect.x() < 0
351 || rect.y() < 0
352 || rect.maxX() > m_surface->size().width()
353 || rect.maxY() > m_surface->size().height())
354 result->zeroFill();
355
356 SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
357 SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), kRGBA_8888_SkColorType, alphaType);
358
359 m_surface->willAccessPixels();
360 context()->readPixels(info, result->data(), 4 * rect.width(), rect.x(), rect.y());
361 return result.release();
362 }
363
putByteArray(Multiply multiplied,Uint8ClampedArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)364 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
365 {
366 if (!isSurfaceValid())
367 return;
368
369 ASSERT(sourceRect.width() > 0);
370 ASSERT(sourceRect.height() > 0);
371
372 int originX = sourceRect.x();
373 int destX = destPoint.x() + sourceRect.x();
374 ASSERT(destX >= 0);
375 ASSERT(destX < m_surface->size().width());
376 ASSERT(originX >= 0);
377 ASSERT(originX < sourceRect.maxX());
378
379 int originY = sourceRect.y();
380 int destY = destPoint.y() + sourceRect.y();
381 ASSERT(destY >= 0);
382 ASSERT(destY < m_surface->size().height());
383 ASSERT(originY >= 0);
384 ASSERT(originY < sourceRect.maxY());
385
386 const size_t srcBytesPerRow = 4 * sourceSize.width();
387 const void* srcAddr = source->data() + originY * srcBytesPerRow + originX * 4;
388 SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
389 SkImageInfo info = SkImageInfo::Make(sourceRect.width(), sourceRect.height(), kRGBA_8888_SkColorType, alphaType);
390
391 m_surface->willAccessPixels();
392
393 context()->writePixels(info, srcAddr, srcBytesPerRow, destX, destY);
394 }
395
396 template <typename T>
encodeImage(T & source,const String & mimeType,const double * quality,Vector<char> * output)397 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
398 {
399 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
400
401 if (mimeType == "image/jpeg") {
402 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
403 if (quality && *quality >= 0.0 && *quality <= 1.0)
404 compressionQuality = static_cast<int>(*quality * 100 + 0.5);
405 if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
406 return false;
407 } else if (mimeType == "image/webp") {
408 int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
409 if (quality && *quality >= 0.0 && *quality <= 1.0)
410 compressionQuality = static_cast<int>(*quality * 100 + 0.5);
411 if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
412 return false;
413 } else {
414 if (!PNGImageEncoder::encode(source, encodedImage))
415 return false;
416 ASSERT(mimeType == "image/png");
417 }
418
419 return true;
420 }
421
toDataURL(const String & mimeType,const double * quality) const422 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
423 {
424 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
425
426 Vector<char> encodedImage;
427 if (!isSurfaceValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
428 return "data:,";
429
430 return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
431 }
432
ImageDataToDataURL(const ImageDataBuffer & imageData,const String & mimeType,const double * quality)433 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
434 {
435 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
436
437 Vector<char> encodedImage;
438 if (!encodeImage(imageData, mimeType, quality, &encodedImage))
439 return "data:,";
440
441 return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
442 }
443
444 } // namespace blink
445