1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28 29#if USE(3D_GRAPHICS) 30 31#include "GraphicsContext3D.h" 32 33#include "BitmapImage.h" 34#include "GraphicsContextCG.h" 35#include "Image.h" 36 37#if HAVE(ARM_NEON_INTRINSICS) 38#include "GraphicsContext3DNEON.h" 39#endif 40 41#include <CoreGraphics/CGBitmapContext.h> 42#include <CoreGraphics/CGContext.h> 43#include <CoreGraphics/CGDataProvider.h> 44#include <CoreGraphics/CGImage.h> 45 46#include <wtf/RetainPtr.h> 47#include <wtf/StdLibExtras.h> 48 49namespace WebCore { 50 51enum SourceDataFormatBase { 52 SourceFormatBaseR = 0, 53 SourceFormatBaseA, 54 SourceFormatBaseRA, 55 SourceFormatBaseAR, 56 SourceFormatBaseRGB, 57 SourceFormatBaseRGBA, 58 SourceFormatBaseARGB, 59 SourceFormatBaseNumFormats 60}; 61 62enum AlphaFormat { 63 AlphaFormatNone = 0, 64 AlphaFormatFirst, 65 AlphaFormatLast, 66 AlphaFormatNumFormats 67}; 68 69// This returns SourceFormatNumFormats if the combination of input parameters is unsupported. 70static GraphicsContext3D::DataFormat getSourceDataFormat(unsigned componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian) 71{ 72 const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { // componentsPerPixel x AlphaFormat 73 // AlphaFormatNone AlphaFormatFirst AlphaFormatLast 74 { SourceFormatBaseR, SourceFormatBaseA, SourceFormatBaseA }, // 1 componentsPerPixel 75 { SourceFormatBaseNumFormats, SourceFormatBaseAR, SourceFormatBaseRA }, // 2 componentsPerPixel 76 { SourceFormatBaseRGB, SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, // 3 componentsPerPixel 77 { SourceFormatBaseNumFormats, SourceFormatBaseARGB, SourceFormatBaseRGBA } // 4 componentsPerPixel 78 }; 79 const static GraphicsContext3D::DataFormat formatTable[SourceFormatBaseNumFormats][4] = { // SourceDataFormatBase x bitsPerComponent x endian 80 // 8bits, little endian 8bits, big endian 16bits, little endian 16bits, big endian 81 { GraphicsContext3D::DataFormatR8, GraphicsContext3D::DataFormatR8, GraphicsContext3D::DataFormatR16Little, GraphicsContext3D::DataFormatR16Big }, 82 { GraphicsContext3D::DataFormatA8, GraphicsContext3D::DataFormatA8, GraphicsContext3D::DataFormatA16Little, GraphicsContext3D::DataFormatA16Big }, 83 { GraphicsContext3D::DataFormatAR8, GraphicsContext3D::DataFormatRA8, GraphicsContext3D::DataFormatRA16Little, GraphicsContext3D::DataFormatRA16Big }, 84 { GraphicsContext3D::DataFormatRA8, GraphicsContext3D::DataFormatAR8, GraphicsContext3D::DataFormatAR16Little, GraphicsContext3D::DataFormatAR16Big }, 85 { GraphicsContext3D::DataFormatBGR8, GraphicsContext3D::DataFormatRGB8, GraphicsContext3D::DataFormatRGB16Little, GraphicsContext3D::DataFormatRGB16Big }, 86 { GraphicsContext3D::DataFormatABGR8, GraphicsContext3D::DataFormatRGBA8, GraphicsContext3D::DataFormatRGBA16Little, GraphicsContext3D::DataFormatRGBA16Big }, 87 { GraphicsContext3D::DataFormatBGRA8, GraphicsContext3D::DataFormatARGB8, GraphicsContext3D::DataFormatARGB16Little, GraphicsContext3D::DataFormatARGB16Big } 88 }; 89 90 ASSERT(componentsPerPixel <= 4 && componentsPerPixel > 0); 91 SourceDataFormatBase formatBase = formatTableBase[componentsPerPixel - 1][alphaFormat]; 92 if (formatBase == SourceFormatBaseNumFormats) 93 return GraphicsContext3D::DataFormatNumFormats; 94 return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)]; 95} 96 97namespace { 98uint8_t convertColor16LittleTo8(uint16_t value) 99{ 100 return value >> 8; 101} 102 103uint8_t convertColor16BigTo8(uint16_t value) 104{ 105 return static_cast<uint8_t>(value & 0x00FF); 106} 107 108template<int format, typename SourceType, typename DstType> 109ALWAYS_INLINE void convert16BitFormatToRGBA8(const SourceType*, DstType*, unsigned) 110{ 111 ASSERT_NOT_REACHED(); 112} 113 114template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 115{ 116#if HAVE(ARM_NEON_INTRINSICS) 117 SIMD::unpackOneRowOfRGBA16LittleToRGBA8(source, destination, pixelsPerRow); 118#endif 119 for (unsigned i = 0; i < pixelsPerRow; ++i) { 120 destination[0] = convertColor16LittleTo8(source[0]); 121 destination[1] = convertColor16LittleTo8(source[1]); 122 destination[2] = convertColor16LittleTo8(source[2]); 123 destination[3] = convertColor16LittleTo8(source[3]); 124 source += 4; 125 destination += 4; 126 } 127} 128 129template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 130{ 131 for (unsigned i = 0; i < pixelsPerRow; ++i) { 132 destination[0] = convertColor16BigTo8(source[0]); 133 destination[1] = convertColor16BigTo8(source[1]); 134 destination[2] = convertColor16BigTo8(source[2]); 135 destination[3] = convertColor16BigTo8(source[3]); 136 source += 4; 137 destination += 4; 138 } 139} 140 141template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 142{ 143#if HAVE(ARM_NEON_INTRINSICS) 144 SIMD::unpackOneRowOfRGB16LittleToRGBA8(source, destination, pixelsPerRow); 145#endif 146 for (unsigned i = 0; i < pixelsPerRow; ++i) { 147 destination[0] = convertColor16LittleTo8(source[0]); 148 destination[1] = convertColor16LittleTo8(source[1]); 149 destination[2] = convertColor16LittleTo8(source[2]); 150 destination[3] = 0xFF; 151 source += 3; 152 destination += 4; 153 } 154} 155 156template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 157{ 158 for (unsigned i = 0; i < pixelsPerRow; ++i) { 159 destination[0] = convertColor16BigTo8(source[0]); 160 destination[1] = convertColor16BigTo8(source[1]); 161 destination[2] = convertColor16BigTo8(source[2]); 162 destination[3] = 0xFF; 163 source += 3; 164 destination += 4; 165 } 166} 167 168template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 169{ 170#if HAVE(ARM_NEON_INTRINSICS) 171 SIMD::unpackOneRowOfARGB16LittleToRGBA8(source, destination, pixelsPerRow); 172#endif 173 for (unsigned i = 0; i < pixelsPerRow; ++i) { 174 destination[0] = convertColor16LittleTo8(source[1]); 175 destination[1] = convertColor16LittleTo8(source[2]); 176 destination[2] = convertColor16LittleTo8(source[3]); 177 destination[3] = convertColor16LittleTo8(source[0]); 178 source += 4; 179 destination += 4; 180 } 181} 182 183template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 184{ 185 for (unsigned i = 0; i < pixelsPerRow; ++i) { 186 destination[0] = convertColor16BigTo8(source[1]); 187 destination[1] = convertColor16BigTo8(source[2]); 188 destination[2] = convertColor16BigTo8(source[3]); 189 destination[3] = convertColor16BigTo8(source[0]); 190 source += 4; 191 destination += 4; 192 } 193} 194 195template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 196{ 197 for (unsigned i = 0; i < pixelsPerRow; ++i) { 198 destination[0] = convertColor16LittleTo8(source[0]); 199 destination[1] = convertColor16LittleTo8(source[0]); 200 destination[2] = convertColor16LittleTo8(source[0]); 201 destination[3] = 0xFF; 202 source += 1; 203 destination += 4; 204 } 205} 206 207template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 208{ 209 for (unsigned i = 0; i < pixelsPerRow; ++i) { 210 destination[0] = convertColor16BigTo8(source[0]); 211 destination[1] = convertColor16BigTo8(source[0]); 212 destination[2] = convertColor16BigTo8(source[0]); 213 destination[3] = 0xFF; 214 source += 1; 215 destination += 4; 216 } 217} 218 219template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 220{ 221 for (unsigned i = 0; i < pixelsPerRow; ++i) { 222 destination[0] = convertColor16LittleTo8(source[0]); 223 destination[1] = convertColor16LittleTo8(source[0]); 224 destination[2] = convertColor16LittleTo8(source[0]); 225 destination[3] = convertColor16LittleTo8(source[1]); 226 source += 2; 227 destination += 4; 228 } 229} 230 231template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 232{ 233 for (unsigned i = 0; i < pixelsPerRow; ++i) { 234 destination[0] = convertColor16BigTo8(source[0]); 235 destination[1] = convertColor16BigTo8(source[0]); 236 destination[2] = convertColor16BigTo8(source[0]); 237 destination[3] = convertColor16BigTo8(source[1]); 238 source += 2; 239 destination += 4; 240 } 241} 242 243template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 244{ 245 for (unsigned i = 0; i < pixelsPerRow; ++i) { 246 destination[0] = convertColor16LittleTo8(source[1]); 247 destination[1] = convertColor16LittleTo8(source[1]); 248 destination[2] = convertColor16LittleTo8(source[1]); 249 destination[3] = convertColor16LittleTo8(source[0]); 250 source += 2; 251 destination += 4; 252 } 253} 254 255template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 256{ 257 for (unsigned i = 0; i < pixelsPerRow; ++i) { 258 destination[0] = convertColor16BigTo8(source[1]); 259 destination[1] = convertColor16BigTo8(source[1]); 260 destination[2] = convertColor16BigTo8(source[1]); 261 destination[3] = convertColor16BigTo8(source[0]); 262 source += 2; 263 destination += 4; 264 } 265} 266 267template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 268{ 269 for (unsigned i = 0; i < pixelsPerRow; ++i) { 270 destination[0] = 0x0; 271 destination[1] = 0x0; 272 destination[2] = 0x0; 273 destination[3] = convertColor16LittleTo8(source[0]); 274 source += 1; 275 destination += 4; 276 } 277} 278 279template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 280{ 281 for (unsigned i = 0; i < pixelsPerRow; ++i) { 282 destination[0] = 0x0; 283 destination[1] = 0x0; 284 destination[2] = 0x0; 285 destination[3] = convertColor16BigTo8(source[0]); 286 source += 1; 287 destination += 4; 288 } 289} 290 291void convert16BitFormatToRGBA8(GraphicsContext3D::DataFormat srcFormat, const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow) 292{ 293#define CONVERT16BITFORMATTORGBA8(SrcFormat) \ 294 case SrcFormat: \ 295 return convert16BitFormatToRGBA8<SrcFormat>(source, destination, pixelsPerRow); 296 297 switch (srcFormat) { 298 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Little) 299 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Big) 300 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Little) 301 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Big) 302 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Little) 303 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Big) 304 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Little) 305 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Big) 306 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Little) 307 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Big) 308 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Little) 309 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Big) 310 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Little) 311 CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Big) 312 default: 313 ASSERT_NOT_REACHED(); 314 } 315#undef CONVERT16BITFORMATTORGBA8 316} 317 318} 319 320GraphicsContext3D::ImageExtractor::~ImageExtractor() 321{ 322} 323 324bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile) 325{ 326 if (!m_image) 327 return false; 328 bool hasAlpha = !m_image->currentFrameKnownToBeOpaque(); 329 if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && m_image->data()) { 330 ImageSource decoder(ImageSource::AlphaNotPremultiplied, 331 ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied); 332 decoder.setData(m_image->data(), true); 333 if (!decoder.frameCount()) 334 return false; 335#if PLATFORM(IOS) 336 float scaleHint = 1; 337 if (m_image->isBitmapImage()) { 338 FloatSize originalSize = toBitmapImage(m_image)->originalSize(); 339 if (!originalSize.isEmpty()) 340 scaleHint = std::min<float>(1, std::max(m_image->size().width() / originalSize.width(), m_image->size().width() / originalSize.height())); 341 } 342 m_decodedImage = adoptCF(decoder.createFrameAtIndex(0, &scaleHint)); 343#else 344 m_decodedImage = adoptCF(decoder.createFrameAtIndex(0)); 345#endif 346 m_cgImage = m_decodedImage.get(); 347 } else 348 m_cgImage = m_image->nativeImageForCurrentFrame(); 349 if (!m_cgImage) 350 return false; 351 352 m_imageWidth = CGImageGetWidth(m_cgImage); 353 m_imageHeight = CGImageGetHeight(m_cgImage); 354 if (!m_imageWidth || !m_imageHeight) 355 return false; 356 357 // See whether the image is using an indexed color space, and if 358 // so, re-render it into an RGB color space. The image re-packing 359 // code requires color data, not color table indices, for the 360 // image data. 361 CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_cgImage); 362 CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace); 363 if (model == kCGColorSpaceModelIndexed) { 364 RetainPtr<CGContextRef> bitmapContext; 365 // FIXME: we should probably manually convert the image by indexing into 366 // the color table, which would allow us to avoid premultiplying the 367 // alpha channel. Creation of a bitmap context with an alpha channel 368 // doesn't seem to work unless it's premultiplied. 369 bitmapContext = adoptCF(CGBitmapContextCreate(0, m_imageWidth, m_imageHeight, 8, m_imageWidth * 4, 370 deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); 371 if (!bitmapContext) 372 return false; 373 374 CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy); 375 CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone); 376 CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, m_imageWidth, m_imageHeight), m_cgImage); 377 378 // Now discard the original CG image and replace it with a copy from the bitmap context. 379 m_decodedImage = adoptCF(CGBitmapContextCreateImage(bitmapContext.get())); 380 m_cgImage = m_decodedImage.get(); 381 } 382 383 size_t bitsPerComponent = CGImageGetBitsPerComponent(m_cgImage); 384 size_t bitsPerPixel = CGImageGetBitsPerPixel(m_cgImage); 385 if (bitsPerComponent != 8 && bitsPerComponent != 16) 386 return false; 387 if (bitsPerPixel % bitsPerComponent) 388 return false; 389 size_t componentsPerPixel = bitsPerPixel / bitsPerComponent; 390 391 CGBitmapInfo bitInfo = CGImageGetBitmapInfo(m_cgImage); 392 bool bigEndianSource = false; 393 // These could technically be combined into one large switch 394 // statement, but we prefer not to so that we fail fast if we 395 // encounter an unexpected image configuration. 396 if (bitsPerComponent == 16) { 397 switch (bitInfo & kCGBitmapByteOrderMask) { 398 case kCGBitmapByteOrder16Big: 399 bigEndianSource = true; 400 break; 401 case kCGBitmapByteOrder16Little: 402 bigEndianSource = false; 403 break; 404 case kCGBitmapByteOrderDefault: 405 // This is a bug in earlier version of cg where the default endian 406 // is little whereas the decoded 16-bit png image data is actually 407 // Big. Later version (10.6.4) no longer returns ByteOrderDefault. 408 bigEndianSource = true; 409 break; 410 default: 411 return false; 412 } 413 } else { 414 switch (bitInfo & kCGBitmapByteOrderMask) { 415 case kCGBitmapByteOrder32Big: 416 bigEndianSource = true; 417 break; 418 case kCGBitmapByteOrder32Little: 419 bigEndianSource = false; 420 break; 421 case kCGBitmapByteOrderDefault: 422 // It appears that the default byte order is actually big 423 // endian even on little endian architectures. 424 bigEndianSource = true; 425 break; 426 default: 427 return false; 428 } 429 } 430 431 m_alphaOp = AlphaDoNothing; 432 AlphaFormat alphaFormat = AlphaFormatNone; 433 switch (CGImageGetAlphaInfo(m_cgImage)) { 434 case kCGImageAlphaPremultipliedFirst: 435 if (!premultiplyAlpha) 436 m_alphaOp = AlphaDoUnmultiply; 437 alphaFormat = AlphaFormatFirst; 438 break; 439 case kCGImageAlphaFirst: 440 // This path is only accessible for MacOS earlier than 10.6.4. 441 if (premultiplyAlpha) 442 m_alphaOp = AlphaDoPremultiply; 443 alphaFormat = AlphaFormatFirst; 444 break; 445 case kCGImageAlphaNoneSkipFirst: 446 // This path is only accessible for MacOS earlier than 10.6.4. 447 alphaFormat = AlphaFormatFirst; 448 break; 449 case kCGImageAlphaPremultipliedLast: 450 if (!premultiplyAlpha) 451 m_alphaOp = AlphaDoUnmultiply; 452 alphaFormat = AlphaFormatLast; 453 break; 454 case kCGImageAlphaLast: 455 if (premultiplyAlpha) 456 m_alphaOp = AlphaDoPremultiply; 457 alphaFormat = AlphaFormatLast; 458 break; 459 case kCGImageAlphaNoneSkipLast: 460 alphaFormat = AlphaFormatLast; 461 break; 462 case kCGImageAlphaNone: 463 alphaFormat = AlphaFormatNone; 464 break; 465 default: 466 return false; 467 } 468 469 m_imageSourceFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource); 470 if (m_imageSourceFormat == DataFormatNumFormats) 471 return false; 472 473 m_pixelData = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(m_cgImage))); 474 if (!m_pixelData) 475 return false; 476 477 m_imagePixelData = reinterpret_cast<const void*>(CFDataGetBytePtr(m_pixelData.get())); 478 479 unsigned int srcUnpackAlignment = 0; 480 size_t bytesPerRow = CGImageGetBytesPerRow(m_cgImage); 481 unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth; 482 if (padding) { 483 srcUnpackAlignment = padding + 1; 484 while (bytesPerRow % srcUnpackAlignment) 485 ++srcUnpackAlignment; 486 } 487 488 m_imageSourceUnpackAlignment = srcUnpackAlignment; 489 // Using a bitmap context created according to destination format and drawing the CGImage to the bitmap context can also do the format conversion, 490 // but it would premultiply the alpha channel as a side effect. 491 // Prefer to mannually Convert 16bit per-component formats to RGBA8 formats instead. 492 if (bitsPerComponent == 16) { 493 m_formalizedRGBA8Data = std::make_unique<uint8_t[]>(m_imageWidth * m_imageHeight * 4); 494 const uint16_t* source = reinterpret_cast<const uint16_t*>(m_imagePixelData); 495 uint8_t* destination = m_formalizedRGBA8Data.get(); 496 const ptrdiff_t srcStrideInElements = bytesPerRow / sizeof(uint16_t); 497 const ptrdiff_t dstStrideInElements = 4 * m_imageWidth; 498 for (unsigned i =0; i < m_imageHeight; i++) { 499 convert16BitFormatToRGBA8(m_imageSourceFormat, source, destination, m_imageWidth); 500 source += srcStrideInElements; 501 destination += dstStrideInElements; 502 } 503 m_imagePixelData = reinterpret_cast<const void*>(m_formalizedRGBA8Data.get()); 504 m_imageSourceFormat = DataFormatRGBA8; 505 m_imageSourceUnpackAlignment = 1; 506 } 507 return true; 508} 509 510static void releaseImageData(void*, const void* data, size_t) 511{ 512 fastFree(const_cast<void*>(data)); 513} 514 515void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, GraphicsContext* context) 516{ 517 if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0 || !context) 518 return; 519 int rowBytes = imageWidth * 4; 520 RetainPtr<CGDataProviderRef> dataProvider; 521 522 if (context->isAcceleratedContext()) { 523 unsigned char* copiedPixels; 524 525 if (!tryFastCalloc(imageHeight, rowBytes).getValue(copiedPixels)) 526 return; 527 528 memcpy(copiedPixels, imagePixels, rowBytes * imageHeight); 529 dataProvider = adoptCF(CGDataProviderCreateWithData(0, copiedPixels, rowBytes * imageHeight, releaseImageData)); 530 } else 531 dataProvider = adoptCF(CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0)); 532 533 RetainPtr<CGImageRef> cgImage = adoptCF(CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, 534 dataProvider.get(), 0, false, kCGRenderingIntentDefault)); 535 536 // CSS styling may cause the canvas's content to be resized on 537 // the page. Go back to the Canvas to figure out the correct 538 // width and height to draw. 539 FloatRect canvasRect(0, 0, canvasWidth, canvasHeight); 540 FloatSize imageSize(imageWidth, imageHeight); 541 // We want to completely overwrite the previous frame's 542 // rendering results. 543 544 GraphicsContextStateSaver stateSaver(*context); 545 context->scale(FloatSize(1, -1)); 546 context->translate(0, -imageHeight); 547 context->setImageInterpolationQuality(InterpolationNone); 548 context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), 1, CompositeCopy); 549} 550 551} // namespace WebCore 552 553#endif // USE(3D_GRAPHICS) 554