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 "platform/MIMETypeRegistry.h"
37 #include "platform/geometry/IntRect.h"
38 #include "platform/graphics/BitmapImage.h"
39 #include "platform/graphics/Extensions3D.h"
40 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/GraphicsContext3D.h"
42 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
43 #include "platform/graphics/gpu/DrawingBuffer.h"
44 #include "platform/graphics/gpu/SharedGraphicsContext3D.h"
45 #include "platform/graphics/skia/NativeImageSkia.h"
46 #include "platform/graphics/skia/SkiaUtils.h"
47 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
48 #include "platform/image-encoders/skia/PNGImageEncoder.h"
49 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
50 #include "public/platform/Platform.h"
51 #include "third_party/skia/include/effects/SkTableColorFilter.h"
52 #include "wtf/MathExtras.h"
53 #include "wtf/text/Base64.h"
54 #include "wtf/text/WTFString.h"
55
56 using namespace std;
57
58 namespace WebCore {
59
create(PassOwnPtr<ImageBufferSurface> surface)60 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
61 {
62 if (!surface->isValid())
63 return nullptr;
64 return adoptPtr(new ImageBuffer(surface));
65 }
66
create(const IntSize & size,OpacityMode opacityMode)67 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
68 {
69 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
70 if (!surface->isValid())
71 return nullptr;
72 return adoptPtr(new ImageBuffer(surface.release()));
73 }
74
ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)75 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
76 : m_surface(surface)
77 {
78 if (m_surface->canvas()) {
79 m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
80 m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
81 m_context->setAccelerated(m_surface->isAccelerated());
82 }
83 }
84
~ImageBuffer()85 ImageBuffer::~ImageBuffer()
86 {
87 }
88
context() const89 GraphicsContext* ImageBuffer::context() const
90 {
91 m_surface->willUse();
92 ASSERT(m_context.get());
93 return m_context.get();
94 }
95
bitmap() const96 const SkBitmap& ImageBuffer::bitmap() const
97 {
98 m_surface->willUse();
99 return m_surface->bitmap();
100 }
101
isValid() const102 bool ImageBuffer::isValid() const
103 {
104 return m_surface->isValid();
105 }
106
deepSkBitmapCopy(const SkBitmap & bitmap)107 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
108 {
109 SkBitmap tmp;
110 if (!bitmap.deepCopyTo(&tmp, bitmap.config()))
111 bitmap.copyTo(&tmp, bitmap.config());
112
113 return tmp;
114 }
115
copyImage(BackingStoreCopy copyBehavior,ScaleBehavior) const116 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
117 {
118 if (!isValid())
119 return BitmapImage::create(NativeImageSkia::create());
120
121 const SkBitmap& bitmap = m_surface->bitmap();
122 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
123 }
124
fastCopyImageMode()125 BackingStoreCopy ImageBuffer::fastCopyImageMode()
126 {
127 return DontCopyBackingStore;
128 }
129
platformLayer() const130 blink::WebLayer* ImageBuffer::platformLayer() const
131 {
132 return m_surface->layer();
133 }
134
copyToPlatformTexture(GraphicsContext3D & context,Platform3DObject texture,GC3Denum internalFormat,GC3Denum destType,GC3Dint level,bool premultiplyAlpha,bool flipY)135 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY)
136 {
137 if (!m_surface->isAccelerated() || !platformLayer() || !isValid())
138 return false;
139
140 if (!context.makeContextCurrent())
141 return false;
142
143 Extensions3D* extensions = context.extensions();
144 if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy")
145 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level))
146 return false;
147
148 // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
149 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
150
151 // The canvas is stored in an inverted position, so the flip semantics are reversed.
152 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, !flipY);
153 extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, getBackingTexture(), texture, level, internalFormat, destType);
154
155 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false);
156 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
157 context.flush();
158 return true;
159 }
160
drawNeedsCopy(GraphicsContext * src,GraphicsContext * dst)161 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
162 {
163 ASSERT(dst);
164 return (src == dst);
165 }
166
getBackingTexture()167 Platform3DObject ImageBuffer::getBackingTexture()
168 {
169 return m_surface->getBackingTexture();
170 }
171
copyRenderingResultsFromDrawingBuffer(DrawingBuffer * drawingBuffer)172 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer)
173 {
174 if (!drawingBuffer)
175 return false;
176 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get();
177 Platform3DObject tex = m_surface->getBackingTexture();
178 if (!context3D || !tex)
179 return false;
180
181 return drawingBuffer->copyToPlatformTexture(*(context3D.get()), tex, GL_RGBA,
182 GL_UNSIGNED_BYTE, 0, true, false);
183 }
184
draw(GraphicsContext * context,const FloatRect & destRect,const FloatRect & srcRect,CompositeOperator op,blink::WebBlendMode blendMode,bool useLowQualityScale)185 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect,
186 CompositeOperator op, blink::WebBlendMode blendMode, bool useLowQualityScale)
187 {
188 if (!isValid())
189 return;
190
191 const SkBitmap& bitmap = m_surface->bitmap();
192 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
193 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale);
194 }
195
flush()196 void ImageBuffer::flush()
197 {
198 if (m_surface->canvas()) {
199 m_surface->canvas()->flush();
200 }
201 }
202
drawPattern(GraphicsContext * context,const FloatRect & srcRect,const FloatSize & scale,const FloatPoint & phase,CompositeOperator op,const FloatRect & destRect,blink::WebBlendMode blendMode,const IntSize & repeatSpacing)203 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
204 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, blink::WebBlendMode blendMode, const IntSize& repeatSpacing)
205 {
206 if (!isValid())
207 return;
208
209 const SkBitmap& bitmap = m_surface->bitmap();
210 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
211 image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
212 }
213
getLinearRgbLUT()214 static const Vector<uint8_t>& getLinearRgbLUT()
215 {
216 DEFINE_STATIC_LOCAL(Vector<uint8_t>, linearRgbLUT, ());
217 if (linearRgbLUT.isEmpty()) {
218 linearRgbLUT.reserveCapacity(256);
219 for (unsigned i = 0; i < 256; i++) {
220 float color = i / 255.0f;
221 color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f));
222 color = std::max(0.0f, color);
223 color = std::min(1.0f, color);
224 linearRgbLUT.append(static_cast<uint8_t>(round(color * 255)));
225 }
226 }
227 return linearRgbLUT;
228 }
229
getDeviceRgbLUT()230 static const Vector<uint8_t>& getDeviceRgbLUT()
231 {
232 DEFINE_STATIC_LOCAL(Vector<uint8_t>, deviceRgbLUT, ());
233 if (deviceRgbLUT.isEmpty()) {
234 deviceRgbLUT.reserveCapacity(256);
235 for (unsigned i = 0; i < 256; i++) {
236 float color = i / 255.0f;
237 color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f;
238 color = std::max(0.0f, color);
239 color = std::min(1.0f, color);
240 deviceRgbLUT.append(static_cast<uint8_t>(round(color * 255)));
241 }
242 }
243 return deviceRgbLUT;
244 }
245
transformColorSpace(ColorSpace srcColorSpace,ColorSpace dstColorSpace)246 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
247 {
248 if (srcColorSpace == dstColorSpace)
249 return;
250
251 // only sRGB <-> linearRGB are supported at the moment
252 if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB)
253 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB))
254 return;
255
256 // FIXME: Disable color space conversions on accelerated canvases (for now).
257 if (context()->isAccelerated() || !isValid())
258 return;
259
260 const SkBitmap& bitmap = m_surface->bitmap();
261 if (bitmap.isNull())
262 return;
263
264 const Vector<uint8_t>& lookUpTable = dstColorSpace == ColorSpaceLinearRGB ?
265 getLinearRgbLUT() : getDeviceRgbLUT();
266
267 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
268 IntSize size = m_surface->size();
269 SkAutoLockPixels bitmapLock(bitmap);
270 for (int y = 0; y < size.height(); ++y) {
271 uint32_t* srcRow = bitmap.getAddr32(0, y);
272 for (int x = 0; x < size.width(); ++x) {
273 SkColor color = SkPMColorToColor(srcRow[x]);
274 srcRow[x] = SkPreMultiplyARGB(
275 SkColorGetA(color),
276 lookUpTable[SkColorGetR(color)],
277 lookUpTable[SkColorGetG(color)],
278 lookUpTable[SkColorGetB(color)]);
279 }
280 }
281 }
282
createColorSpaceFilter(ColorSpace srcColorSpace,ColorSpace dstColorSpace)283 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
284 ColorSpace dstColorSpace)
285 {
286 if ((srcColorSpace == dstColorSpace)
287 || (srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB)
288 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB))
289 return 0;
290
291 const uint8_t* lut = 0;
292 if (dstColorSpace == ColorSpaceLinearRGB)
293 lut = &getLinearRgbLUT()[0];
294 else if (dstColorSpace == ColorSpaceDeviceRGB)
295 lut = &getDeviceRgbLUT()[0];
296 else
297 return 0;
298
299 return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
300 }
301
302 template <Multiply multiplied>
getImageData(const IntRect & rect,GraphicsContext * context,const IntSize & size)303 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size)
304 {
305 float area = 4.0f * rect.width() * rect.height();
306 if (area > static_cast<float>(std::numeric_limits<int>::max()))
307 return 0;
308
309 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
310
311 unsigned char* data = result->data();
312
313 if (rect.x() < 0
314 || rect.y() < 0
315 || rect.maxX() > size.width()
316 || rect.maxY() > size.height())
317 result->zeroFill();
318
319 unsigned destBytesPerRow = 4 * rect.width();
320 SkBitmap destBitmap;
321 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(), destBytesPerRow);
322 destBitmap.setPixels(data);
323
324 SkCanvas::Config8888 config8888;
325 if (multiplied == Premultiplied)
326 config8888 = SkCanvas::kRGBA_Premul_Config8888;
327 else
328 config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
329
330 context->readPixels(&destBitmap, rect.x(), rect.y(), config8888);
331 return result.release();
332 }
333
getUnmultipliedImageData(const IntRect & rect) const334 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
335 {
336 if (!isValid())
337 return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
338 return getImageData<Unmultiplied>(rect, context(), m_surface->size());
339 }
340
getPremultipliedImageData(const IntRect & rect) const341 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
342 {
343 if (!isValid())
344 return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
345 return getImageData<Premultiplied>(rect, context(), m_surface->size());
346 }
347
putByteArray(Multiply multiplied,Uint8ClampedArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)348 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
349 {
350 if (!isValid())
351 return;
352
353 ASSERT(sourceRect.width() > 0);
354 ASSERT(sourceRect.height() > 0);
355
356 int originX = sourceRect.x();
357 int destX = destPoint.x() + sourceRect.x();
358 ASSERT(destX >= 0);
359 ASSERT(destX < m_surface->size().width());
360 ASSERT(originX >= 0);
361 ASSERT(originX < sourceRect.maxX());
362
363 int endX = destPoint.x() + sourceRect.maxX();
364 ASSERT(endX <= m_surface->size().width());
365
366 int numColumns = endX - destX;
367
368 int originY = sourceRect.y();
369 int destY = destPoint.y() + sourceRect.y();
370 ASSERT(destY >= 0);
371 ASSERT(destY < m_surface->size().height());
372 ASSERT(originY >= 0);
373 ASSERT(originY < sourceRect.maxY());
374
375 int endY = destPoint.y() + sourceRect.maxY();
376 ASSERT(endY <= m_surface->size().height());
377 int numRows = endY - destY;
378
379 unsigned srcBytesPerRow = 4 * sourceSize.width();
380 SkBitmap srcBitmap;
381 srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
382 srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4);
383
384 SkCanvas::Config8888 config8888;
385 if (multiplied == Premultiplied)
386 config8888 = SkCanvas::kRGBA_Premul_Config8888;
387 else
388 config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
389
390 context()->writePixels(srcBitmap, destX, destY, config8888);
391 }
392
393 template <typename T>
encodeImage(T & source,const String & mimeType,const double * quality,Vector<char> * output)394 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
395 {
396 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
397
398 if (mimeType == "image/jpeg") {
399 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
400 if (quality && *quality >= 0.0 && *quality <= 1.0)
401 compressionQuality = static_cast<int>(*quality * 100 + 0.5);
402 if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
403 return false;
404 } else if (mimeType == "image/webp") {
405 int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
406 if (quality && *quality >= 0.0 && *quality <= 1.0)
407 compressionQuality = static_cast<int>(*quality * 100 + 0.5);
408 if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
409 return false;
410 } else {
411 if (!PNGImageEncoder::encode(source, encodedImage))
412 return false;
413 ASSERT(mimeType == "image/png");
414 }
415
416 return true;
417 }
418
toDataURL(const String & mimeType,const double * quality) const419 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
420 {
421 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
422
423 Vector<char> encodedImage;
424 if (!isValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
425 return "data:,";
426 Vector<char> base64Data;
427 base64Encode(encodedImage, base64Data);
428
429 return "data:" + mimeType + ";base64," + base64Data;
430 }
431
ImageDataToDataURL(const ImageDataBuffer & imageData,const String & mimeType,const double * quality)432 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
433 {
434 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
435
436 Vector<char> encodedImage;
437 if (!encodeImage(imageData, mimeType, quality, &encodedImage))
438 return "data:,";
439
440 Vector<char> base64Data;
441 base64Encode(encodedImage, base64Data);
442
443 return "data:" + mimeType + ";base64," + base64Data;
444 }
445
446 } // namespace WebCore
447