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