/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Vladimir Vukicevic * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "gfxImageSurface.h" #include "gfxUtils.h" static PRUint8 sUnpremultiplyTable[256*256]; static PRUint8 sPremultiplyTable[256*256]; static PRBool sTablesInitialized = PR_FALSE; static const PRUint8 PremultiplyValue(PRUint8 a, PRUint8 v) { return sPremultiplyTable[a*256+v]; } static const PRUint8 UnpremultiplyValue(PRUint8 a, PRUint8 v) { return sUnpremultiplyTable[a*256+v]; } static void CalculateTables() { // It's important that the array be indexed first by alpha and then by rgb // value. When we unpremultiply a pixel, we're guaranteed to do three // lookups with the same alpha; indexing by alpha first makes it likely that // those three lookups will be close to one another in memory, thus // increasing the chance of a cache hit. // Unpremultiply table // a == 0 case for (PRUint32 c = 0; c <= 255; c++) { sUnpremultiplyTable[c] = c; } for (int a = 1; a <= 255; a++) { for (int c = 0; c <= 255; c++) { sUnpremultiplyTable[a*256+c] = (PRUint8)((c * 255) / a); } } // Premultiply table for (int a = 0; a <= 255; a++) { for (int c = 0; c <= 255; c++) { sPremultiplyTable[a*256+c] = (a * c + 254) / 255; } } sTablesInitialized = PR_TRUE; } void gfxUtils::PremultiplyImageSurface(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface) { if (!aDestSurface) aDestSurface = aSourceSurface; NS_ASSERTION(aSourceSurface->Format() == aDestSurface->Format() && aSourceSurface->Width() == aDestSurface->Width() && aSourceSurface->Height() == aDestSurface->Height() && aSourceSurface->Stride() == aDestSurface->Stride(), "Source and destination surfaces don't have identical characteristics"); NS_ASSERTION(aSourceSurface->Stride() == aSourceSurface->Width() * 4, "Source surface stride isn't tightly packed"); // Only premultiply ARGB32 if (aSourceSurface->Format() != gfxASurface::ImageFormatARGB32) { if (aDestSurface != aSourceSurface) { memcpy(aDestSurface->Data(), aSourceSurface->Data(), aSourceSurface->Stride() * aSourceSurface->Height()); } return; } if (!sTablesInitialized) CalculateTables(); PRUint8 *src = aSourceSurface->Data(); PRUint8 *dst = aDestSurface->Data(); PRUint32 dim = aSourceSurface->Width() * aSourceSurface->Height(); for (PRUint32 i = 0; i < dim; ++i) { #ifdef IS_LITTLE_ENDIAN PRUint8 b = *src++; PRUint8 g = *src++; PRUint8 r = *src++; PRUint8 a = *src++; *dst++ = PremultiplyValue(a, b); *dst++ = PremultiplyValue(a, g); *dst++ = PremultiplyValue(a, r); *dst++ = a; #else PRUint8 a = *src++; PRUint8 r = *src++; PRUint8 g = *src++; PRUint8 b = *src++; *dst++ = a; *dst++ = PremultiplyValue(a, r); *dst++ = PremultiplyValue(a, g); *dst++ = PremultiplyValue(a, b); #endif } } void gfxUtils::UnpremultiplyImageSurface(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface) { if (!aDestSurface) aDestSurface = aSourceSurface; NS_ASSERTION(aSourceSurface->Format() == aDestSurface->Format() && aSourceSurface->Width() == aDestSurface->Width() && aSourceSurface->Height() == aDestSurface->Height() && aSourceSurface->Stride() == aDestSurface->Stride(), "Source and destination surfaces don't have identical characteristics"); NS_ASSERTION(aSourceSurface->Stride() == aSourceSurface->Width() * 4, "Source surface stride isn't tightly packed"); // Only premultiply ARGB32 if (aSourceSurface->Format() != gfxASurface::ImageFormatARGB32) { if (aDestSurface != aSourceSurface) { memcpy(aDestSurface->Data(), aSourceSurface->Data(), aSourceSurface->Stride() * aSourceSurface->Height()); } return; } if (!sTablesInitialized) CalculateTables(); PRUint8 *src = aSourceSurface->Data(); PRUint8 *dst = aDestSurface->Data(); PRUint32 dim = aSourceSurface->Width() * aSourceSurface->Height(); for (PRUint32 i = 0; i < dim; ++i) { #ifdef IS_LITTLE_ENDIAN PRUint8 b = *src++; PRUint8 g = *src++; PRUint8 r = *src++; PRUint8 a = *src++; *dst++ = UnpremultiplyValue(a, b); *dst++ = UnpremultiplyValue(a, g); *dst++ = UnpremultiplyValue(a, r); *dst++ = a; #else PRUint8 a = *src++; PRUint8 r = *src++; PRUint8 g = *src++; PRUint8 b = *src++; *dst++ = a; *dst++ = UnpremultiplyValue(a, r); *dst++ = UnpremultiplyValue(a, g); *dst++ = UnpremultiplyValue(a, b); #endif } }