void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) { const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace); if (!lookUpTable) return; // FIXME: Disable color space conversions on accelerated canvases (for now). if (context()->isAccelerated() || !isSurfaceValid()) return; const SkBitmap& bitmap = m_surface->bitmap(); if (bitmap.isNull()) return; ASSERT(bitmap.colorType() == kN32_SkColorType); IntSize size = m_surface->size(); SkAutoLockPixels bitmapLock(bitmap); for (int y = 0; y < size.height(); ++y) { uint32_t* srcRow = bitmap.getAddr32(0, y); for (int x = 0; x < size.width(); ++x) { SkColor color = SkPMColorToColor(srcRow[x]); srcRow[x] = SkPreMultiplyARGB( SkColorGetA(color), lookUpTable[SkColorGetR(color)], lookUpTable[SkColorGetG(color)], lookUpTable[SkColorGetB(color)]); } } }
bool ImageFrameGenerator::decodeAndScale(size_t index, const SkImageInfo& info, void* pixels, size_t rowBytes) { // Prevent concurrent decode or scale operations on the same image data. MutexLocker lock(m_decodeMutex); if (m_decodeFailed) return false; TRACE_EVENT1("blink", "ImageFrameGenerator::decodeAndScale", "frame index", static_cast<int>(index)); m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes)); // This implementation does not support scaling so check the requested size. SkISize scaledSize = SkISize::Make(info.width(), info.height()); ASSERT(m_fullSize == scaledSize); SkBitmap bitmap = tryToResumeDecode(index, scaledSize); if (bitmap.isNull()) return false; // Don't keep the allocator because it contains a pointer to memory // that we do not own. m_externalAllocator.clear(); // Check to see if the decoder has written directly to the pixel memory // provided. If not, make a copy. ASSERT(bitmap.width() == scaledSize.width()); ASSERT(bitmap.height() == scaledSize.height()); SkAutoLockPixels bitmapLock(bitmap); if (bitmap.getPixels() != pixels) return bitmap.copyPixelsTo(pixels, rowBytes * info.height(), rowBytes); return true; }
bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsigned char>* output) { SkAutoLockPixels bitmapLock(bitmap); if (bitmap.colorType() != kPMColor_SkColorType || !bitmap.getPixels()) return false; // Only support 32 bit/pixel skia bitmaps. return encodePixels(IntSize(bitmap.width(), bitmap.height()), static_cast<unsigned char *>(bitmap.getPixels()), true, quality, output); }
void GraphicsContext3DInternal::paintRenderingResultsToCanvas(CanvasRenderingContext* context) { HTMLCanvasElement* canvas = context->canvas(); ImageBuffer* imageBuffer = canvas->buffer(); unsigned char* pixels = 0; #if PLATFORM(SKIA) const SkBitmap* canvasBitmap = imageBuffer->context()->platformContext()->bitmap(); const SkBitmap* readbackBitmap = 0; ASSERT(canvasBitmap->config() == SkBitmap::kARGB_8888_Config); if (canvasBitmap->width() == m_impl->width() && canvasBitmap->height() == m_impl->height()) { // This is the fastest and most common case. We read back // directly into the canvas's backing store. readbackBitmap = canvasBitmap; m_resizingBitmap.reset(); } else { // We need to allocate a temporary bitmap for reading back the // pixel data. We will then use Skia to rescale this bitmap to // the size of the canvas's backing store. if (m_resizingBitmap.width() != m_impl->width() || m_resizingBitmap.height() != m_impl->height()) { m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, m_impl->width(), m_impl->height()); if (!m_resizingBitmap.allocPixels()) return; } readbackBitmap = &m_resizingBitmap; } // Read back the frame buffer. SkAutoLockPixels bitmapLock(*readbackBitmap); pixels = static_cast<unsigned char*>(readbackBitmap->getPixels()); #elif PLATFORM(CG) if (m_renderOutput) pixels = m_renderOutput; #else #error Must port to your platform #endif m_impl->readBackFramebuffer(pixels, 4 * m_impl->width() * m_impl->height()); #if PLATFORM(SKIA) if (m_resizingBitmap.readyToDraw()) { // We need to draw the resizing bitmap into the canvas's backing store. SkCanvas canvas(*canvasBitmap); SkRect dst; dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap->width()), SkIntToScalar(canvasBitmap->height())); canvas.drawBitmapRect(m_resizingBitmap, 0, dst); } #elif PLATFORM(CG) if (m_renderOutput && context->is3d()) { WebGLRenderingContext* webGLContext = static_cast<WebGLRenderingContext*>(context); webGLContext->graphicsContext3D()->paintToCanvas(m_renderOutput, m_impl->width(), m_impl->height(), canvas->width(), canvas->height(), imageBuffer->context()->platformContext()); } #else #error Must port to your platform #endif }
bool PNGImageEncoder::encode(const SkBitmap& bitmap, Vector<unsigned char>* output) { SkAutoLockPixels bitmapLock(bitmap); if (bitmap.config() != SkBitmap::kARGB_8888_Config) return false; // Only support 32 bit/pixel skia bitmaps. return encodePixels(IntSize(bitmap.width(), bitmap.height()), static_cast<unsigned char*>(bitmap.getPixels()), true, output); }
PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { ASSERT(context()); RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); unsigned char* data = result->data()->data().data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) memset(data, 0, result->data()->length()); int originx = rect.x(); int destx = 0; if (originx < 0) { destx = -originx; originx = 0; } int endx = rect.x() + rect.width(); if (endx > m_size.width()) endx = m_size.width(); int numColumns = endx - originx; int originy = rect.y(); int desty = 0; if (originy < 0) { desty = -originy; originy = 0; } int endy = rect.y() + rect.height(); if (endy > m_size.height()) endy = m_size.height(); int numRows = endy - originy; const SkBitmap& bitmap = *context()->platformContext()->bitmap(); ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRow = data + desty * destBytesPerRow + destx * 4; for (int y = 0; y < numRows; ++y) { uint32_t* srcRow = bitmap.getAddr32(originx, originy + y); for (int x = 0; x < numColumns; ++x) { SkColor color = SkPMColorToColor(srcRow[x]); unsigned char* destPixel = &destRow[x * 4]; destPixel[0] = SkColorGetR(color); destPixel[1] = SkColorGetG(color); destPixel[2] = SkColorGetB(color); destPixel[3] = SkColorGetA(color); } destRow += destBytesPerRow; } return result; }
bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsigned char>* output) { if (bitmap.config() != SkBitmap::kARGB_8888_Config) return false; // Only support ARGB 32 bpp skia bitmaps. SkAutoLockPixels bitmapLock(bitmap); IntSize imageSize(bitmap.width(), bitmap.height()); imageSize.clampNegativeToZero(); JPEGOutputBuffer destination; destination.output = output; Vector<JSAMPLE> row; jpeg_compress_struct cinfo; jpeg_error_mgr error; cinfo.err = jpeg_std_error(&error); error.error_exit = handleError; jmp_buf jumpBuffer; cinfo.client_data = &jumpBuffer; if (setjmp(jumpBuffer)) { jpeg_destroy_compress(&cinfo); return false; } jpeg_create_compress(&cinfo); cinfo.dest = &destination; cinfo.dest->init_destination = prepareOutput; cinfo.dest->empty_output_buffer = writeOutput; cinfo.dest->term_destination = finishOutput; cinfo.image_height = imageSize.height(); cinfo.image_width = imageSize.width(); cinfo.in_color_space = JCS_RGB; cinfo.input_components = 3; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); const SkPMColor* pixels = static_cast<SkPMColor*>(bitmap.getPixels()); row.resize(cinfo.image_width * cinfo.input_components); while (cinfo.next_scanline < cinfo.image_height) { preMultipliedBGRAtoRGB(pixels, cinfo.image_width, row.data()); jpeg_write_scanlines(&cinfo, row.dataSlot(), 1); pixels += cinfo.image_width; } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); return true; }
void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& destPoint, const SkBitmap& bitmap, const IntSize& size) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originX = sourceRect.x(); int destX = destPoint.x() + sourceRect.x(); ASSERT(destX >= 0); ASSERT(destX < size.width()); ASSERT(originX >= 0); ASSERT(originX < sourceRect.right()); int endX = destPoint.x() + sourceRect.right(); ASSERT(endX <= size.width()); int numColumns = endX - destX; int originY = sourceRect.y(); int destY = destPoint.y() + sourceRect.y(); ASSERT(destY >= 0); ASSERT(destY < size.height()); ASSERT(originY >= 0); ASSERT(originY < sourceRect.bottom()); int endY = destPoint.y() + sourceRect.bottom(); ASSERT(endY <= size.height()); int numRows = endY - destY; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned srcBytesPerRow = 4 * source->width(); const unsigned char* srcRow = source->data()->data()->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destX, destY + y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; if (multiplied == Unmultiplied) destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); else destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } }
void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originx = sourceRect.x(); int destx = destPoint.x() + sourceRect.x(); ASSERT(destx >= 0); ASSERT(destx < m_size.width()); ASSERT(originx >= 0); ASSERT(originx < sourceRect.right()); int endx = destPoint.x() + sourceRect.right(); ASSERT(endx <= m_size.width()); int numColumns = endx - destx; int originy = sourceRect.y(); int desty = destPoint.y() + sourceRect.y(); ASSERT(desty >= 0); ASSERT(desty < m_size.height()); ASSERT(originy >= 0); ASSERT(originy < sourceRect.bottom()); int endy = destPoint.y() + sourceRect.bottom(); ASSERT(endy <= m_size.height()); int numRows = endy - desty; const SkBitmap& bitmap = *context()->platformContext()->bitmap(); ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned srcBytesPerRow = 4 * source->width(); const unsigned char* srcRow = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destx, desty + y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; destRow[x] = SkPreMultiplyARGB(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } }
void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) { const SkBitmap& bitmap = *context()->platformContext()->bitmap(); if (bitmap.isNull()) return; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); for (int y = 0; y < m_size.height(); ++y) { uint32_t* srcRow = bitmap.getAddr32(0, y); for (int x = 0; x < m_size.width(); ++x) { SkColor color = SkPMColorToColor(srcRow[x]); srcRow[x] = SkPreMultiplyARGB(SkColorGetA(color), lookUpTable[SkColorGetR(color)], lookUpTable[SkColorGetG(color)], lookUpTable[SkColorGetB(color)]); } } }
void DrawingBuffer::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer) { unsigned char* pixels = 0; const SkBitmap& canvasBitmap = imageBuffer->bitmap(); const SkBitmap* readbackBitmap = 0; ASSERT(canvasBitmap.config() == SkBitmap::kARGB_8888_Config); if (canvasBitmap.width() == width && canvasBitmap.height() == height) { // This is the fastest and most common case. We read back // directly into the canvas's backing store. readbackBitmap = &canvasBitmap; m_resizingBitmap.reset(); } else { // We need to allocate a temporary bitmap for reading back the // pixel data. We will then use Skia to rescale this bitmap to // the size of the canvas's backing store. if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) { m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); if (!m_resizingBitmap.allocPixels()) return; } readbackBitmap = &m_resizingBitmap; } // Read back the frame buffer. SkAutoLockPixels bitmapLock(*readbackBitmap); pixels = static_cast<unsigned char*>(readbackBitmap->getPixels()); m_context->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); readBackFramebuffer(pixels, width, height, ReadbackSkia, premultiplyAlpha ? WebGLImageConversion::AlphaDoPremultiply : WebGLImageConversion::AlphaDoNothing); flipVertically(pixels, width, height); readbackBitmap->notifyPixelsChanged(); if (m_resizingBitmap.readyToDraw()) { // We need to draw the resizing bitmap into the canvas's backing store. SkCanvas canvas(canvasBitmap); SkRect dst; dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap.width()), SkIntToScalar(canvasBitmap.height())); canvas.drawBitmapRect(m_resizingBitmap, 0, dst); } }
bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, void* pixels, size_t rowBytes) { // This method is called to populate a discardable memory owned by Skia. // Prevents concurrent decode or scale operations on the same image data. MutexLocker lock(m_decodeMutex); // This implementation does not support scaling so check the requested size. SkISize scaledSize = SkISize::Make(info.width(), info.height()); ASSERT(m_fullSize == scaledSize); if (m_decodeFailedAndEmpty) return false; TRACE_EVENT2("blink", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", m_decodeCount); m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes)); SkBitmap bitmap = tryToResumeDecode(scaledSize, index); if (bitmap.isNull()) return false; // Don't keep the allocator because it contains a pointer to memory // that we do not own. m_externalAllocator.clear(); ASSERT(bitmap.width() == scaledSize.width()); ASSERT(bitmap.height() == scaledSize.height()); bool result = true; SkAutoLockPixels bitmapLock(bitmap); // Check to see if decoder has written directly to the memory provided // by Skia. If not make a copy. if (bitmap.getPixels() != pixels) result = bitmap.copyPixelsTo(pixels, rowBytes * info.height(), rowBytes); return result; }
PassRefPtr<ImageData> getImageData(const IntRect& rect, const SkBitmap& bitmap, const IntSize& size) { RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); if (bitmap.config() == SkBitmap::kNo_Config) { // This is an empty SkBitmap that could not be configured. ASSERT(!size.width() || !size.height()); return result; } unsigned char* data = result->data()->data()->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height()) memset(data, 0, result->data()->length()); int originX = rect.x(); int destX = 0; if (originX < 0) { destX = -originX; originX = 0; } int endX = rect.x() + rect.width(); if (endX > size.width()) endX = size.width(); int numColumns = endX - originX; int originY = rect.y(); int destY = 0; if (originY < 0) { destY = -originY; originY = 0; } int endY = rect.y() + rect.height(); if (endY > size.height()) endY = size.height(); int numRows = endY - originY; ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); SkAutoLockPixels bitmapLock(bitmap); unsigned destBytesPerRow = 4 * rect.width(); unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* srcRow = bitmap.getAddr32(originX, originY + y); for (int x = 0; x < numColumns; ++x) { unsigned char* destPixel = &destRow[x * 4]; if (multiplied == Unmultiplied) { SkColor color = srcRow[x]; unsigned a = SkColorGetA(color); destPixel[0] = a ? SkColorGetR(color) * 255 / a : 0; destPixel[1] = a ? SkColorGetG(color) * 255 / a : 0; destPixel[2] = a ? SkColorGetB(color) * 255 / a : 0; destPixel[3] = a; } else { // Input and output are both pre-multiplied, we just need to re-arrange the // bytes from the bitmap format to RGBA. destPixel[0] = SkGetPackedR32(srcRow[x]); destPixel[1] = SkGetPackedG32(srcRow[x]); destPixel[2] = SkGetPackedB32(srcRow[x]); destPixel[3] = SkGetPackedA32(srcRow[x]); } } destRow += destBytesPerRow; } return result; }
void GraphicsContext3DPrivate::paintFramebufferToCanvas(int framebuffer, int width, int height, bool premultiplyAlpha, ImageBuffer* imageBuffer) { unsigned char* pixels = 0; size_t bufferSize = 4 * width * height; #if USE(SKIA) const SkBitmap* canvasBitmap = imageBuffer->context()->platformContext()->bitmap(); const SkBitmap* readbackBitmap = 0; ASSERT(canvasBitmap->config() == SkBitmap::kARGB_8888_Config); if (canvasBitmap->width() == width && canvasBitmap->height() == height) { // This is the fastest and most common case. We read back // directly into the canvas's backing store. readbackBitmap = canvasBitmap; m_resizingBitmap.reset(); } else { // We need to allocate a temporary bitmap for reading back the // pixel data. We will then use Skia to rescale this bitmap to // the size of the canvas's backing store. if (m_resizingBitmap.width() != width || m_resizingBitmap.height() != height) { m_resizingBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); if (!m_resizingBitmap.allocPixels()) return; } readbackBitmap = &m_resizingBitmap; } // Read back the frame buffer. SkAutoLockPixels bitmapLock(*readbackBitmap); pixels = static_cast<unsigned char*>(readbackBitmap->getPixels()); #elif USE(CG) if (!m_renderOutput || m_renderOutputSize != bufferSize) { m_renderOutput = adoptArrayPtr(new unsigned char[bufferSize]); m_renderOutputSize = bufferSize; } pixels = m_renderOutput.get(); #else #error Must port to your platform #endif m_impl->readBackFramebuffer(pixels, 4 * width * height, framebuffer, width, height); if (premultiplyAlpha) { for (size_t i = 0; i < bufferSize; i += 4) { pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255); pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255); pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255); } } #if USE(SKIA) readbackBitmap->notifyPixelsChanged(); if (m_resizingBitmap.readyToDraw()) { // We need to draw the resizing bitmap into the canvas's backing store. SkCanvas canvas(*canvasBitmap); SkRect dst; dst.set(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(canvasBitmap->width()), SkIntToScalar(canvasBitmap->height())); canvas.drawBitmapRect(m_resizingBitmap, 0, dst); } #elif USE(CG) GraphicsContext3D::paintToCanvas(pixels, width, height, imageBuffer->width(), imageBuffer->height(), imageBuffer->context()->platformContext()); #else #error Must port to your platform #endif }