/* vim:set sw=2 sts=2 et cin: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include "nsImageToPixbuf.h" #include "imgIContainer.h" #include "mozilla/gfx/2D.h" #include "mozilla/RefPtr.h" #include "GRefPtr.h" #include "nsCOMPtr.h" using mozilla::gfx::DataSourceSurface; using mozilla::gfx::SurfaceFormat; inline unsigned char unpremultiply(unsigned char color, unsigned char alpha) { if (alpha == 0) return 0; // plus alpha/2 to round instead of truncate return (color * 255 + alpha / 2) / alpha; } already_AddRefed nsImageToPixbuf::ImageToPixbuf( imgIContainer* aImage, const mozilla::Maybe& aOverrideSize) { RefPtr surface; const uint32_t flags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY; if (aOverrideSize) { surface = aImage->GetFrameAtSize(*aOverrideSize, imgIContainer::FRAME_CURRENT, flags); } else { surface = aImage->GetFrame(imgIContainer::FRAME_CURRENT, flags); } // If the last call failed, it was probably because our call stack originates // in an imgINotificationObserver event, meaning that we're not allowed // request a sync decode. Presumably the originating event is something // sensible like OnStopFrame(), so we can just retry the call without a sync // decode. if (!surface) { if (aOverrideSize) { surface = aImage->GetFrameAtSize(*aOverrideSize, imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_NONE); } else { surface = aImage->GetFrame(imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_NONE); } } NS_ENSURE_TRUE(surface, nullptr); return SourceSurfaceToPixbuf(surface, surface->GetSize().width, surface->GetSize().height); } already_AddRefed nsImageToPixbuf::SourceSurfaceToPixbuf( SourceSurface* aSurface, int32_t aWidth, int32_t aHeight) { MOZ_ASSERT(aWidth <= aSurface->GetSize().width && aHeight <= aSurface->GetSize().height, "Requested rect is bigger than the supplied surface"); RefPtr pixbuf = dont_AddRef(gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, aWidth, aHeight)); if (!pixbuf) { return nullptr; } uint32_t destStride = gdk_pixbuf_get_rowstride(pixbuf); guchar* destPixels = gdk_pixbuf_get_pixels(pixbuf); RefPtr dataSurface = aSurface->GetDataSurface(); DataSourceSurface::MappedSurface map; if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { return nullptr; } uint8_t* srcData = map.mData; int32_t srcStride = map.mStride; SurfaceFormat format = dataSurface->GetFormat(); for (int32_t row = 0; row < aHeight; ++row) { for (int32_t col = 0; col < aWidth; ++col) { guchar* destPixel = destPixels + row * destStride + 4 * col; uint32_t* srcPixel = reinterpret_cast((srcData + row * srcStride + 4 * col)); if (format == SurfaceFormat::B8G8R8A8) { const uint8_t a = (*srcPixel >> 24) & 0xFF; const uint8_t r = unpremultiply((*srcPixel >> 16) & 0xFF, a); const uint8_t g = unpremultiply((*srcPixel >> 8) & 0xFF, a); const uint8_t b = unpremultiply((*srcPixel >> 0) & 0xFF, a); *destPixel++ = r; *destPixel++ = g; *destPixel++ = b; *destPixel++ = a; } else { MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8); const uint8_t r = (*srcPixel >> 16) & 0xFF; const uint8_t g = (*srcPixel >> 8) & 0xFF; const uint8_t b = (*srcPixel >> 0) & 0xFF; *destPixel++ = r; *destPixel++ = g; *destPixel++ = b; *destPixel++ = 0xFF; // A } } } dataSurface->Unmap(); return pixbuf.forget(); }