OLD | NEW |
| (Empty) |
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 "core/platform/graphics/ImageBuffer.h" | |
35 | |
36 #include "core/platform/graphics/BitmapImage.h" | |
37 #include "core/platform/graphics/Extensions3D.h" | |
38 #include "core/platform/graphics/GaneshUtils.h" | |
39 #include "core/platform/graphics/GraphicsContext3D.h" | |
40 #include "core/platform/graphics/gpu/DrawingBuffer.h" | |
41 #include "core/platform/graphics/gpu/SharedGraphicsContext3D.h" | |
42 #include "core/platform/graphics/skia/NativeImageSkia.h" | |
43 #include "core/platform/graphics/skia/SkiaUtils.h" | |
44 #include "core/platform/image-encoders/skia/JPEGImageEncoder.h" | |
45 #include "core/platform/image-encoders/skia/PNGImageEncoder.h" | |
46 #include "core/platform/image-encoders/skia/WEBPImageEncoder.h" | |
47 #include "platform/MIMETypeRegistry.h" | |
48 #include "platform/geometry/IntRect.h" | |
49 #include "public/platform/Platform.h" | |
50 #include "skia/ext/platform_canvas.h" | |
51 #include "third_party/skia/include/core/SkBitmapDevice.h" | |
52 #include "third_party/skia/include/core/SkColorFilter.h" | |
53 #include "third_party/skia/include/core/SkColorPriv.h" | |
54 #include "third_party/skia/include/core/SkSurface.h" | |
55 #include "third_party/skia/include/effects/SkTableColorFilter.h" | |
56 #include "third_party/skia/include/gpu/GrContext.h" | |
57 #include "third_party/skia/include/gpu/SkGpuDevice.h" | |
58 #include "third_party/skia/include/gpu/SkGrPixelRef.h" | |
59 #include "wtf/MathExtras.h" | |
60 #include "wtf/text/Base64.h" | |
61 #include "wtf/text/WTFString.h" | |
62 | |
63 using namespace std; | |
64 | |
65 namespace WebCore { | |
66 | |
67 static PassRefPtr<SkCanvas> createAcceleratedCanvas(const IntSize& size, Canvas2
DLayerBridgePtr* outLayerBridge, OpacityMode opacityMode, int msaaSampleCount) | |
68 { | |
69 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get(); | |
70 if (!context3D) | |
71 return 0; | |
72 Canvas2DLayerBridge::OpacityMode bridgeOpacityMode = opacityMode == Opaque ?
Canvas2DLayerBridge::Opaque : Canvas2DLayerBridge::NonOpaque; | |
73 *outLayerBridge = Canvas2DLayerBridge::create(context3D.release(), size, bri
dgeOpacityMode, msaaSampleCount); | |
74 // If canvas buffer allocation failed, debug build will have asserted | |
75 // For release builds, we must verify whether the device has a render target | |
76 return (*outLayerBridge) ? (*outLayerBridge)->getCanvas() : 0; | |
77 } | |
78 | |
79 static PassRefPtr<SkCanvas> createTextureBackedCanvas(const IntSize& size) | |
80 { | |
81 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get(); | |
82 if (!context3D) | |
83 return 0; | |
84 GrContext* gr = context3D->grContext(); | |
85 if (!gr) | |
86 return 0; | |
87 SkBitmap* bitmap = new SkBitmap; | |
88 if (!bitmap || !ensureTextureBackedSkBitmap(gr, *bitmap, size, kDefault_GrSu
rfaceOrigin, kRGBA_8888_GrPixelConfig)) | |
89 return 0; | |
90 return adoptRef(new SkCanvas(*bitmap)); | |
91 } | |
92 | |
93 static PassRefPtr<SkCanvas> createNonPlatformCanvas(const IntSize& size) | |
94 { | |
95 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(SkBitmap::kARGB_8888_Co
nfig, size.width(), size.height())); | |
96 SkPixelRef* pixelRef = device->accessBitmap(false).pixelRef(); | |
97 return adoptRef(pixelRef ? new SkCanvas(device) : 0); | |
98 } | |
99 | |
100 PassOwnPtr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const IntSize& size,
float resolutionScale, const GraphicsContext* context, bool hasAlpha) | |
101 { | |
102 bool success = false; | |
103 OwnPtr<ImageBuffer> buf = adoptPtr(new ImageBuffer(size, resolutionScale, co
ntext, hasAlpha, success)); | |
104 if (!success) | |
105 return nullptr; | |
106 return buf.release(); | |
107 } | |
108 | |
109 PassOwnPtr<ImageBuffer> ImageBuffer::createBufferForTile(const FloatSize& tileSi
ze, const FloatSize& clampedTileSize, RenderingMode renderingMode) | |
110 { | |
111 IntSize imageSize(roundedIntSize(clampedTileSize)); | |
112 IntSize unclampedImageSize(roundedIntSize(tileSize)); | |
113 | |
114 // Don't create empty ImageBuffers. | |
115 if (imageSize.isEmpty()) | |
116 return nullptr; | |
117 | |
118 OwnPtr<ImageBuffer> image = ImageBuffer::create(imageSize, 1, renderingMode)
; | |
119 if (!image) | |
120 return nullptr; | |
121 | |
122 GraphicsContext* imageContext = image->context(); | |
123 ASSERT(imageContext); | |
124 | |
125 // Compensate rounding effects, as the absolute target rect is using floatin
g-point numbers and the image buffer size is integer. | |
126 imageContext->scale(FloatSize(unclampedImageSize.width() / tileSize.width(),
unclampedImageSize.height() / tileSize.height())); | |
127 | |
128 return image.release(); | |
129 } | |
130 | |
131 ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, const Graph
icsContext* compatibleContext, bool hasAlpha, bool& success) | |
132 : m_size(size) | |
133 , m_logicalSize(size) | |
134 , m_resolutionScale(resolutionScale) | |
135 { | |
136 if (!compatibleContext) { | |
137 success = false; | |
138 return; | |
139 } | |
140 | |
141 SkAutoTUnref<SkBaseDevice> device(compatibleContext->createCompatibleDevice(
size, hasAlpha)); | |
142 if (!device.get()) { | |
143 success = false; | |
144 return; | |
145 } | |
146 | |
147 SkPixelRef* pixelRef = device->accessBitmap(false).pixelRef(); | |
148 if (!pixelRef) { | |
149 success = false; | |
150 return; | |
151 } | |
152 | |
153 m_canvas = adoptRef(new SkCanvas(device)); | |
154 m_context = adoptPtr(new GraphicsContext(m_canvas.get())); | |
155 m_context->setCertainlyOpaque(!hasAlpha); | |
156 m_context->scale(FloatSize(m_resolutionScale, m_resolutionScale)); | |
157 | |
158 success = true; | |
159 } | |
160 | |
161 ImageBuffer::ImageBuffer(const IntSize& size, float resolutionScale, RenderingMo
de renderingMode, OpacityMode opacityMode, int acceleratedSampleCount, bool& suc
cess) | |
162 : m_size(size) | |
163 , m_logicalSize(size) | |
164 , m_resolutionScale(resolutionScale) | |
165 { | |
166 if (renderingMode == Accelerated) { | |
167 m_canvas = createAcceleratedCanvas(size, &m_layerBridge, opacityMode, ac
celeratedSampleCount); | |
168 if (!m_canvas) | |
169 renderingMode = UnacceleratedNonPlatformBuffer; | |
170 } | |
171 | |
172 if (renderingMode == TextureBacked) { | |
173 m_canvas = createTextureBackedCanvas(size); | |
174 if (!m_canvas) | |
175 renderingMode = UnacceleratedNonPlatformBuffer; | |
176 } | |
177 | |
178 if (renderingMode == UnacceleratedNonPlatformBuffer) | |
179 m_canvas = createNonPlatformCanvas(size); | |
180 | |
181 if (!m_canvas) | |
182 m_canvas = adoptRef(skia::TryCreateBitmapCanvas(size.width(), size.heigh
t(), false)); | |
183 | |
184 if (!m_canvas) { | |
185 success = false; | |
186 return; | |
187 } | |
188 | |
189 m_context = adoptPtr(new GraphicsContext(m_canvas.get())); | |
190 m_context->setCertainlyOpaque(opacityMode == Opaque); | |
191 m_context->setAccelerated(renderingMode == Accelerated); | |
192 m_context->scale(FloatSize(m_resolutionScale, m_resolutionScale)); | |
193 | |
194 // Clear the background transparent or opaque, as required. It would be nice
if this wasn't | |
195 // required, but the canvas is currently filled with the magic transparency | |
196 // color. Can we have another way to manage this? | |
197 if (renderingMode != TextureBacked) { | |
198 if (opacityMode == Opaque) | |
199 m_canvas->drawARGB(255, 0, 0, 0, SkXfermode::kSrc_Mode); | |
200 else | |
201 m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); | |
202 } | |
203 | |
204 success = true; | |
205 } | |
206 | |
207 ImageBuffer::~ImageBuffer() | |
208 { | |
209 } | |
210 | |
211 GraphicsContext* ImageBuffer::context() const | |
212 { | |
213 if (m_layerBridge) { | |
214 // We're using context acquisition as a signal that someone is about to
render into our buffer and we need | |
215 // to be ready. This isn't logically const-correct, hence the cast. | |
216 const_cast<Canvas2DLayerBridge*>(m_layerBridge.get())->contextAcquired()
; | |
217 } | |
218 return m_context.get(); | |
219 } | |
220 | |
221 | |
222 bool ImageBuffer::isValid() const | |
223 { | |
224 if (m_layerBridge) | |
225 return const_cast<Canvas2DLayerBridge*>(m_layerBridge.get())->isValid(); | |
226 return true; | |
227 } | |
228 | |
229 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap) | |
230 { | |
231 SkBitmap tmp; | |
232 if (!bitmap.deepCopyTo(&tmp, bitmap.config())) | |
233 bitmap.copyTo(&tmp, bitmap.config()); | |
234 | |
235 return tmp; | |
236 } | |
237 | |
238 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBeh
avior) const | |
239 { | |
240 if (!isValid()) | |
241 return BitmapImage::create(NativeImageSkia::create()); | |
242 | |
243 const SkBitmap& bitmap = *context()->bitmap(); | |
244 // FIXME: Start honoring ScaleBehavior to scale 2x buffers down to 1x. | |
245 return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBacki
ngStore ? deepSkBitmapCopy(bitmap) : bitmap, m_resolutionScale)); | |
246 } | |
247 | |
248 BackingStoreCopy ImageBuffer::fastCopyImageMode() | |
249 { | |
250 return DontCopyBackingStore; | |
251 } | |
252 | |
253 blink::WebLayer* ImageBuffer::platformLayer() const | |
254 { | |
255 return m_layerBridge ? m_layerBridge->layer() : 0; | |
256 } | |
257 | |
258 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DOb
ject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool pr
emultiplyAlpha, bool flipY) | |
259 { | |
260 if (!m_layerBridge || !platformLayer() || !isValid()) | |
261 return false; | |
262 | |
263 Platform3DObject sourceTexture = m_layerBridge->backBufferTexture(); | |
264 | |
265 if (!context.makeContextCurrent()) | |
266 return false; | |
267 | |
268 Extensions3D* extensions = context.extensions(); | |
269 if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->suppor
ts("GL_CHROMIUM_flipy") | |
270 || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, leve
l)) | |
271 return false; | |
272 | |
273 // The canvas is stored in a premultiplied format, so unpremultiply if neces
sary. | |
274 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !prem
ultiplyAlpha); | |
275 | |
276 // The canvas is stored in an inverted position, so the flip semantics are r
eversed. | |
277 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, !flipY); | |
278 | |
279 extensions->copyTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D, sourceTexture
, texture, level, internalFormat, destType); | |
280 | |
281 context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false); | |
282 context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false
); | |
283 context.flush(); | |
284 return true; | |
285 } | |
286 | |
287 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst) | |
288 { | |
289 return (src == dst); | |
290 } | |
291 | |
292 Platform3DObject ImageBuffer::getBackingTexture() | |
293 { | |
294 if (!m_context || !m_context->bitmap()) | |
295 return 0; | |
296 const SkBitmap& bitmap = *m_context->bitmap(); | |
297 if (bitmap.getTexture()) | |
298 return (bitmap.getTexture())->getTextureHandle(); | |
299 return 0; | |
300 } | |
301 | |
302 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBu
ffer) | |
303 { | |
304 if (!drawingBuffer) | |
305 return false; | |
306 RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get(); | |
307 Platform3DObject tex = getBackingTexture(); | |
308 if (!context3D || !tex) | |
309 return false; | |
310 | |
311 return drawingBuffer->copyToPlatformTexture(*(context3D.get()), tex, Graphic
sContext3D::RGBA, | |
312 GraphicsContext3D::UNSIGNED_BYTE, 0, true, false); | |
313 } | |
314 | |
315 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, cons
t FloatRect& srcRect, | |
316 CompositeOperator op, blink::WebBlendMode blendMode, bool useLowQualityScale
) | |
317 { | |
318 if (!isValid()) | |
319 return; | |
320 | |
321 const SkBitmap& bitmap = *m_context->bitmap(); | |
322 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsC
opy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); | |
323 context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespe
ctImageOrientation, useLowQualityScale); | |
324 } | |
325 | |
326 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect
, const FloatSize& scale, | |
327 const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, bl
ink::WebBlendMode blendMode, const IntSize& repeatSpacing) | |
328 { | |
329 if (!isValid()) | |
330 return; | |
331 | |
332 const SkBitmap& bitmap = *m_context->bitmap(); | |
333 RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsC
opy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap)); | |
334 image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode,
repeatSpacing); | |
335 } | |
336 | |
337 static const Vector<uint8_t>& getLinearRgbLUT() | |
338 { | |
339 DEFINE_STATIC_LOCAL(Vector<uint8_t>, linearRgbLUT, ()); | |
340 if (linearRgbLUT.isEmpty()) { | |
341 linearRgbLUT.reserveCapacity(256); | |
342 for (unsigned i = 0; i < 256; i++) { | |
343 float color = i / 255.0f; | |
344 color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) /
1.055f, 2.4f)); | |
345 color = std::max(0.0f, color); | |
346 color = std::min(1.0f, color); | |
347 linearRgbLUT.append(static_cast<uint8_t>(round(color * 255))); | |
348 } | |
349 } | |
350 return linearRgbLUT; | |
351 } | |
352 | |
353 static const Vector<uint8_t>& getDeviceRgbLUT() | |
354 { | |
355 DEFINE_STATIC_LOCAL(Vector<uint8_t>, deviceRgbLUT, ()); | |
356 if (deviceRgbLUT.isEmpty()) { | |
357 deviceRgbLUT.reserveCapacity(256); | |
358 for (unsigned i = 0; i < 256; i++) { | |
359 float color = i / 255.0f; | |
360 color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f; | |
361 color = std::max(0.0f, color); | |
362 color = std::min(1.0f, color); | |
363 deviceRgbLUT.append(static_cast<uint8_t>(round(color * 255))); | |
364 } | |
365 } | |
366 return deviceRgbLUT; | |
367 } | |
368 | |
369 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstCo
lorSpace) | |
370 { | |
371 if (srcColorSpace == dstColorSpace) | |
372 return; | |
373 | |
374 // only sRGB <-> linearRGB are supported at the moment | |
375 if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDevi
ceRGB) | |
376 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceD
eviceRGB)) | |
377 return; | |
378 | |
379 // FIXME: Disable color space conversions on accelerated canvases (for now). | |
380 if (context()->isAccelerated() || !isValid()) | |
381 return; | |
382 | |
383 const SkBitmap& bitmap = *context()->bitmap(); | |
384 if (bitmap.isNull()) | |
385 return; | |
386 | |
387 const Vector<uint8_t>& lookUpTable = dstColorSpace == ColorSpaceLinearRGB ? | |
388 getLinearRgbLUT() : getDeviceRgbLUT(); | |
389 | |
390 ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); | |
391 SkAutoLockPixels bitmapLock(bitmap); | |
392 for (int y = 0; y < m_size.height(); ++y) { | |
393 uint32_t* srcRow = bitmap.getAddr32(0, y); | |
394 for (int x = 0; x < m_size.width(); ++x) { | |
395 SkColor color = SkPMColorToColor(srcRow[x]); | |
396 srcRow[x] = SkPreMultiplyARGB( | |
397 SkColorGetA(color), | |
398 lookUpTable[SkColorGetR(color)], | |
399 lookUpTable[SkColorGetG(color)], | |
400 lookUpTable[SkColorGetB(color)]); | |
401 } | |
402 } | |
403 } | |
404 | |
405 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColo
rSpace, | |
406 ColorSpace dstColorSpace) | |
407 { | |
408 if ((srcColorSpace == dstColorSpace) | |
409 || (srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceD
eviceRGB) | |
410 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceD
eviceRGB)) | |
411 return 0; | |
412 | |
413 const uint8_t* lut = 0; | |
414 if (dstColorSpace == ColorSpaceLinearRGB) | |
415 lut = &getLinearRgbLUT()[0]; | |
416 else if (dstColorSpace == ColorSpaceDeviceRGB) | |
417 lut = &getDeviceRgbLUT()[0]; | |
418 else | |
419 return 0; | |
420 | |
421 return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut)); | |
422 } | |
423 | |
424 template <Multiply multiplied> | |
425 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext*
context, const IntSize& size) | |
426 { | |
427 float area = 4.0f * rect.width() * rect.height(); | |
428 if (area > static_cast<float>(std::numeric_limits<int>::max())) | |
429 return 0; | |
430 | |
431 RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(re
ct.width() * rect.height() * 4); | |
432 | |
433 unsigned char* data = result->data(); | |
434 | |
435 if (rect.x() < 0 | |
436 || rect.y() < 0 | |
437 || rect.maxX() > size.width() | |
438 || rect.maxY() > size.height()) | |
439 result->zeroFill(); | |
440 | |
441 unsigned destBytesPerRow = 4 * rect.width(); | |
442 SkBitmap destBitmap; | |
443 destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(
), destBytesPerRow); | |
444 destBitmap.setPixels(data); | |
445 | |
446 SkCanvas::Config8888 config8888; | |
447 if (multiplied == Premultiplied) | |
448 config8888 = SkCanvas::kRGBA_Premul_Config8888; | |
449 else | |
450 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; | |
451 | |
452 context->readPixels(&destBitmap, rect.x(), rect.y(), config8888); | |
453 return result.release(); | |
454 } | |
455 | |
456 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRec
t& rect, CoordinateSystem) const | |
457 { | |
458 if (!isValid()) | |
459 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); | |
460 return getImageData<Unmultiplied>(rect, context(), m_size); | |
461 } | |
462 | |
463 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRe
ct& rect, CoordinateSystem) const | |
464 { | |
465 if (!isValid()) | |
466 return Uint8ClampedArray::create(rect.width() * rect.height() * 4); | |
467 return getImageData<Premultiplied>(rect, context(), m_size); | |
468 } | |
469 | |
470 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, c
onst IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint,
CoordinateSystem) | |
471 { | |
472 if (!isValid()) | |
473 return; | |
474 | |
475 ASSERT(sourceRect.width() > 0); | |
476 ASSERT(sourceRect.height() > 0); | |
477 | |
478 int originX = sourceRect.x(); | |
479 int destX = destPoint.x() + sourceRect.x(); | |
480 ASSERT(destX >= 0); | |
481 ASSERT(destX < m_size.width()); | |
482 ASSERT(originX >= 0); | |
483 ASSERT(originX < sourceRect.maxX()); | |
484 | |
485 int endX = destPoint.x() + sourceRect.maxX(); | |
486 ASSERT(endX <= m_size.width()); | |
487 | |
488 int numColumns = endX - destX; | |
489 | |
490 int originY = sourceRect.y(); | |
491 int destY = destPoint.y() + sourceRect.y(); | |
492 ASSERT(destY >= 0); | |
493 ASSERT(destY < m_size.height()); | |
494 ASSERT(originY >= 0); | |
495 ASSERT(originY < sourceRect.maxY()); | |
496 | |
497 int endY = destPoint.y() + sourceRect.maxY(); | |
498 ASSERT(endY <= m_size.height()); | |
499 int numRows = endY - destY; | |
500 | |
501 unsigned srcBytesPerRow = 4 * sourceSize.width(); | |
502 SkBitmap srcBitmap; | |
503 srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcByt
esPerRow); | |
504 srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4)
; | |
505 | |
506 SkCanvas::Config8888 config8888; | |
507 if (multiplied == Premultiplied) | |
508 config8888 = SkCanvas::kRGBA_Premul_Config8888; | |
509 else | |
510 config8888 = SkCanvas::kRGBA_Unpremul_Config8888; | |
511 | |
512 context()->writePixels(srcBitmap, destX, destY, config8888); | |
513 } | |
514 | |
515 template <typename T> | |
516 static bool encodeImage(T& source, const String& mimeType, const double* quality
, Vector<char>* output) | |
517 { | |
518 Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>
*>(output); | |
519 | |
520 if (mimeType == "image/jpeg") { | |
521 int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; | |
522 if (quality && *quality >= 0.0 && *quality <= 1.0) | |
523 compressionQuality = static_cast<int>(*quality * 100 + 0.5); | |
524 if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage)) | |
525 return false; | |
526 } else if (mimeType == "image/webp") { | |
527 int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality; | |
528 if (quality && *quality >= 0.0 && *quality <= 1.0) | |
529 compressionQuality = static_cast<int>(*quality * 100 + 0.5); | |
530 if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage)) | |
531 return false; | |
532 } else { | |
533 if (!PNGImageEncoder::encode(source, encodedImage)) | |
534 return false; | |
535 ASSERT(mimeType == "image/png"); | |
536 } | |
537 | |
538 return true; | |
539 } | |
540 | |
541 String ImageBuffer::toDataURL(const String& mimeType, const double* quality, Coo
rdinateSystem) const | |
542 { | |
543 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); | |
544 | |
545 Vector<char> encodedImage; | |
546 if (!isValid() || !encodeImage(*context()->bitmap(), mimeType, quality, &enc
odedImage)) | |
547 return "data:,"; | |
548 Vector<char> base64Data; | |
549 base64Encode(encodedImage, base64Data); | |
550 | |
551 return "data:" + mimeType + ";base64," + base64Data; | |
552 } | |
553 | |
554 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeTy
pe, const double* quality) | |
555 { | |
556 ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); | |
557 | |
558 Vector<char> encodedImage; | |
559 if (!encodeImage(imageData, mimeType, quality, &encodedImage)) | |
560 return "data:,"; | |
561 | |
562 Vector<char> base64Data; | |
563 base64Encode(encodedImage, base64Data); | |
564 | |
565 return "data:" + mimeType + ";base64," + base64Data; | |
566 } | |
567 | |
568 } // namespace WebCore | |
OLD | NEW |