зеркало из https://github.com/mozilla/pjs.git
Bug 732985 - Part 1: Add image scaling code to Azure. r=jrmuizel
This commit is contained in:
Родитель
c1743d4c69
Коммит
813df48faf
|
@ -804,6 +804,8 @@ protected:
|
|||
class GFX2D_API Factory
|
||||
{
|
||||
public:
|
||||
static bool HasSSE2();
|
||||
|
||||
static TemporaryRef<DrawTarget> CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface);
|
||||
|
||||
static TemporaryRef<DrawTarget>
|
||||
|
|
|
@ -75,6 +75,84 @@
|
|||
PRLogModuleInfo *sGFX2DLog = PR_NewLogModule("gfx2d");
|
||||
#endif
|
||||
|
||||
// The following code was largely taken from xpcom/glue/SSE.cpp and
|
||||
// made a little simpler.
|
||||
enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
|
||||
|
||||
#ifdef HAVE_CPUID_H
|
||||
|
||||
// cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
|
||||
#include <cpuid.h>
|
||||
|
||||
static bool
|
||||
HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
|
||||
{
|
||||
unsigned int regs[4];
|
||||
return __get_cpuid(level, ®s[0], ®s[1], ®s[2], ®s[3]) &&
|
||||
(regs[reg] & bit);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(_M_IX86) || defined(_M_AMD64))
|
||||
// MSVC 2005 or newer on x86-32 or x86-64
|
||||
#include <intrin.h>
|
||||
#elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
|
||||
|
||||
// Define a function identical to MSVC function.
|
||||
#ifdef __i386
|
||||
static void
|
||||
__cpuid(int CPUInfo[4], int InfoType)
|
||||
{
|
||||
asm (
|
||||
"xchg %esi, %ebx\n"
|
||||
"cpuid\n"
|
||||
"movl %eax, (%edi)\n"
|
||||
"movl %ebx, 4(%edi)\n"
|
||||
"movl %ecx, 8(%edi)\n"
|
||||
"movl %edx, 12(%edi)\n"
|
||||
"xchg %esi, %ebx\n"
|
||||
:
|
||||
: "a"(InfoType), // %eax
|
||||
"D"(CPUInfo) // %edi
|
||||
: "%ecx", "%edx", "%esi"
|
||||
);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
__cpuid(int CPUInfo[4], int InfoType)
|
||||
{
|
||||
asm (
|
||||
"xchg %rsi, %rbx\n"
|
||||
"cpuid\n"
|
||||
"movl %eax, (%rdi)\n"
|
||||
"movl %ebx, 4(%rdi)\n"
|
||||
"movl %ecx, 8(%rdi)\n"
|
||||
"movl %edx, 12(%rdi)\n"
|
||||
"xchg %rsi, %rbx\n"
|
||||
:
|
||||
: "a"(InfoType), // %eax
|
||||
"D"(CPUInfo) // %rdi
|
||||
: "%ecx", "%edx", "%rsi"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static bool
|
||||
HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
|
||||
{
|
||||
// Check that the level in question is supported.
|
||||
volatile int regs[4];
|
||||
__cpuid((int *)regs, level & 0x80000000u);
|
||||
if (unsigned(regs[0]) < level)
|
||||
return false;
|
||||
__cpuid((int *)regs, level);
|
||||
return !!(unsigned(regs[reg]) & bit);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
|
@ -85,6 +163,16 @@ int sGfxLogLevel = LOG_DEBUG;
|
|||
ID3D10Device1 *Factory::mD3D10Device;
|
||||
#endif
|
||||
|
||||
bool
|
||||
Factory::HasSSE2()
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
return HasCPUIDBit(1u, edx, (1u<<26));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
TemporaryRef<DrawTarget>
|
||||
Factory::CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*
|
||||
* 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 "ImageScaling.h"
|
||||
#include "2D.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
inline uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
// Prepare half-adder work
|
||||
uint32_t sum = a ^ b ^ c;
|
||||
uint32_t carry = (a & b) | (a & c) | (b & c);
|
||||
|
||||
// Before shifting, mask lower order bits of each byte to avoid underflow.
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Add d to sum and divide by 2.
|
||||
sum = (((sum ^ d) & mask) >> 1) + (sum & d);
|
||||
|
||||
// Sum is now shifted into place relative to carry, add them together.
|
||||
return (((sum ^ carry) & mask) >> 1) + (sum & carry);
|
||||
}
|
||||
|
||||
inline uint32_t Avg2(uint32_t a, uint32_t b)
|
||||
{
|
||||
// Prepare half-adder work
|
||||
uint32_t sum = a ^ b;
|
||||
uint32_t carry = (a & b);
|
||||
|
||||
// Before shifting, mask lower order bits of each byte to avoid underflow.
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Add d to sum and divide by 2.
|
||||
return ((sum & mask) >> 1) + carry;
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::ScaleForSize(const IntSize &aSize)
|
||||
{
|
||||
uint32_t horizontalDownscales = 0;
|
||||
uint32_t verticalDownscales = 0;
|
||||
|
||||
IntSize scaleSize = mOrigSize;
|
||||
while ((scaleSize.height / 2) > aSize.height) {
|
||||
verticalDownscales++;
|
||||
scaleSize.height /= 2;
|
||||
}
|
||||
|
||||
while ((scaleSize.width / 2) > aSize.width) {
|
||||
horizontalDownscales++;
|
||||
scaleSize.width /= 2;
|
||||
}
|
||||
|
||||
if (scaleSize == mOrigSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
IntSize internalSurfSize;
|
||||
|
||||
internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2);
|
||||
internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2);
|
||||
|
||||
mStride = internalSurfSize.width * 4;
|
||||
if (mStride % 16) {
|
||||
mStride += 16 - (mStride % 16);
|
||||
}
|
||||
|
||||
delete [] mDataStorage;
|
||||
// Allocate 15 bytes extra to make sure we can get 16 byte alignment. We
|
||||
// should add tools for this, see bug 751696.
|
||||
mDataStorage = new uint8_t[internalSurfSize.height * mStride + 15];
|
||||
|
||||
if (uintptr_t(mDataStorage) % 16) {
|
||||
// Our storage does not start at a 16-byte boundary. Make sure mData does!
|
||||
mData = (uint8_t*)(uintptr_t(mDataStorage) +
|
||||
(16 - (uintptr_t(mDataStorage) % 16)));
|
||||
} else {
|
||||
mData = mDataStorage;
|
||||
}
|
||||
|
||||
mSize = scaleSize;
|
||||
|
||||
/* The surface we sample from might not be even sized, if it's not we will
|
||||
* ignore the last row/column. This means we lose some data but it keeps the
|
||||
* code very simple. There's also no perfect answer that provides a better
|
||||
* solution.
|
||||
*/
|
||||
IntSize currentSampledSize = mOrigSize;
|
||||
uint32_t currentSampledStride = mOrigStride;
|
||||
uint8_t *currentSampledData = mOrigData;
|
||||
|
||||
while (verticalDownscales && horizontalDownscales) {
|
||||
if (currentSampledSize.width % 2) {
|
||||
currentSampledSize.width -= 1;
|
||||
}
|
||||
if (currentSampledSize.height % 2) {
|
||||
currentSampledSize.height -= 1;
|
||||
}
|
||||
|
||||
HalfImage2D(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
verticalDownscales--;
|
||||
horizontalDownscales--;
|
||||
currentSampledSize.width /= 2;
|
||||
currentSampledSize.height /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
|
||||
while (verticalDownscales) {
|
||||
if (currentSampledSize.height % 2) {
|
||||
currentSampledSize.height -= 1;
|
||||
}
|
||||
|
||||
HalfImageVertical(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
verticalDownscales--;
|
||||
currentSampledSize.height /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
|
||||
|
||||
while (horizontalDownscales) {
|
||||
if (currentSampledSize.width % 2) {
|
||||
currentSampledSize.width -= 1;
|
||||
}
|
||||
|
||||
HalfImageHorizontal(currentSampledData, currentSampledStride, currentSampledSize,
|
||||
mData, mStride);
|
||||
|
||||
horizontalDownscales--;
|
||||
currentSampledSize.width /= 2;
|
||||
currentSampledData = mData;
|
||||
currentSampledStride = mStride;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImage2D_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImage2D_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImageVertical_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImageVertical_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
#ifdef USE_SSE2
|
||||
if (Factory::HasSSE2()) {
|
||||
HalfImageHorizontal_SSE2(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
HalfImageHorizontal_C(aSource, aSourceStride, aSourceSize, aDest, aDestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x += 2) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
*storage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
|
||||
*(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + (y / 2) * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x++) {
|
||||
uint32_t *upperRow = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
uint32_t *lowerRow = (uint32_t*)(aSource + ((y + 1) * aSourceStride + x * 4));
|
||||
|
||||
*storage++ = Avg2(*upperRow, *lowerRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y++) {
|
||||
uint32_t *storage = (uint32_t*)(aDest + y * aDestStride);
|
||||
for (int x = 0; x < aSourceSize.width; x+= 2) {
|
||||
uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
*storage++ = Avg2(*pixels, *(pixels + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef _MOZILLA_GFX_IMAGESCALING_H
|
||||
#define _MOZILLA_GFX_IMAGESCALING_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#include <vector>
|
||||
#include "Point.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class ImageHalfScaler
|
||||
{
|
||||
public:
|
||||
ImageHalfScaler(uint8_t *aData, int32_t aStride, const IntSize &aSize)
|
||||
: mOrigData(aData), mOrigStride(aStride), mOrigSize(aSize)
|
||||
, mDataStorage(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~ImageHalfScaler()
|
||||
{
|
||||
delete [] mDataStorage;
|
||||
}
|
||||
|
||||
void ScaleForSize(const IntSize &aSize);
|
||||
|
||||
uint8_t *GetScaledData() const { return mData; }
|
||||
IntSize GetSize() const { return mSize; }
|
||||
uint32_t GetStride() const { return mStride; }
|
||||
|
||||
private:
|
||||
void HalfImage2D(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
// This is our SSE2 scaling function. Our destination must always be 16-byte
|
||||
// aligned and use a 16-byte aligned stride.
|
||||
void HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
void HalfImage2D_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageVertical_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
void HalfImageHorizontal_C(uint8_t *aSource, int32_t aSourceStride, const IntSize &aSourceSize,
|
||||
uint8_t *aDest, uint32_t aDestStride);
|
||||
|
||||
uint8_t *mOrigData;
|
||||
int32_t mOrigStride;
|
||||
IntSize mOrigSize;
|
||||
|
||||
uint8_t *mDataStorage;
|
||||
// Guaranteed 16-byte aligned
|
||||
uint8_t *mData;
|
||||
IntSize mSize;
|
||||
// Guaranteed 16-byte aligned
|
||||
uint32_t mStride;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,377 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*
|
||||
* 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 "ImageScaling.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
/* The functions below use the following system for averaging 4 pixels:
|
||||
*
|
||||
* The first observation is that a half-adder is implemented as follows:
|
||||
* R = S + 2C or in the case of a and b (a ^ b) + ((a & b) << 1);
|
||||
*
|
||||
* This can be trivially extended to three pixels by observaring that when
|
||||
* doing (a ^ b ^ c) as the sum, the carry is simply the bitwise-or of the
|
||||
* carries of the individual numbers, since the sum of 3 bits can only ever
|
||||
* have a carry of one.
|
||||
*
|
||||
* We then observe that the average is then ((carry << 1) + sum) >> 1, or,
|
||||
* assuming eliminating overflows and underflows, carry + (sum >> 1).
|
||||
*
|
||||
* We now average our existing sum with the fourth number, so we get:
|
||||
* sum2 = (sum + d) >> 1 or (sum >> 1) + (d >> 1).
|
||||
*
|
||||
* We now observe that our sum has been moved into place relative to the
|
||||
* carry, so we can now average with the carry to get the final 4 input
|
||||
* average: avg = (sum2 + carry) >> 1;
|
||||
*
|
||||
* Or to reverse the proof:
|
||||
* avg = ((sum >> 1) + carry + d >> 1) >> 1
|
||||
* avg = ((a + b + c) >> 1 + d >> 1) >> 1
|
||||
* avg = ((a + b + c + d) >> 2)
|
||||
*
|
||||
* An additional fact used in the SSE versions is the concept that we can
|
||||
* trivially convert a rounded average to a truncated average:
|
||||
*
|
||||
* We have:
|
||||
* f(a, b) = (a + b + 1) >> 1
|
||||
*
|
||||
* And want:
|
||||
* g(a, b) = (a + b) >> 1
|
||||
*
|
||||
* Observe:
|
||||
* ~f(~a, ~b) == ~((~a + ~b + 1) >> 1)
|
||||
* == ~((-a - 1 + -b - 1 + 1) >> 1)
|
||||
* == ~((-a - 1 + -b) >> 1)
|
||||
* == ~((-(a + b) - 1) >> 1)
|
||||
* == ~((~(a + b)) >> 1)
|
||||
* == (a + b) >> 1
|
||||
* == g(a, b)
|
||||
*/
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i not(__m128i arg)
|
||||
{
|
||||
__m128i minusone = _mm_set1_epi32(0xffffffff);
|
||||
return _mm_xor_si128(arg, minusone);
|
||||
}
|
||||
|
||||
/* We have to pass pointers here, MSVC does not allow passing more than 3
|
||||
* __m128i arguments on the stack. And it does not allow 16-byte aligned
|
||||
* stack variables. This inlines properly on MSVC 2010. It does -not- inline
|
||||
* with just the inline directive.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_8x2(__m128i *a, __m128i *b, __m128i *c, __m128i *d)
|
||||
{
|
||||
#define shuf1 _MM_SHUFFLE(2, 0, 2, 0)
|
||||
#define shuf2 _MM_SHUFFLE(3, 1, 3, 1)
|
||||
|
||||
// This cannot be an inline function as the __Imm argument to _mm_shuffle_ps
|
||||
// needs to be a compile time constant.
|
||||
#define shuffle_si128(arga, argb, imm) \
|
||||
_mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps((arga)), _mm_castsi128_ps((argb)), (imm)));
|
||||
|
||||
__m128i t = shuffle_si128(*a, *b, shuf1);
|
||||
*b = shuffle_si128(*a, *b, shuf2);
|
||||
*a = t;
|
||||
t = shuffle_si128(*c, *d, shuf1);
|
||||
*d = shuffle_si128(*c, *d, shuf2);
|
||||
*c = t;
|
||||
|
||||
#undef shuf1
|
||||
#undef shuf2
|
||||
#undef shuffle_si128
|
||||
|
||||
__m128i sum = _mm_xor_si128(*a, _mm_xor_si128(*b, *c));
|
||||
|
||||
__m128i carry = _mm_or_si128(_mm_and_si128(*a, *b), _mm_or_si128(_mm_and_si128(*a, *c), _mm_and_si128(*b, *c)));
|
||||
|
||||
__m128i minusone = _mm_set1_epi32(0xffffffff);
|
||||
|
||||
sum = _mm_avg_epu8(not(sum), not(*d));
|
||||
|
||||
return not(_mm_avg_epu8(sum, not(carry)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_4x2_4x1(__m128i a, __m128i b)
|
||||
{
|
||||
__m128i minusone = _mm_set1_epi32(0xffffffff);
|
||||
|
||||
return not(_mm_avg_epu8(not(a), not(b)));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE __m128i avg_sse2_8x1_4x1(__m128i a, __m128i b)
|
||||
{
|
||||
__m128i t = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(b), _mm_castsi128_ps(a), _MM_SHUFFLE(3, 1, 3, 1)));
|
||||
b = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(b), _mm_castsi128_ps(a), _MM_SHUFFLE(2, 0, 2, 0)));
|
||||
a = t;
|
||||
|
||||
__m128i minusone = _mm_set1_epi32(0xffffffff);
|
||||
|
||||
return not(_mm_avg_epu8(not(a), not(b)));
|
||||
}
|
||||
|
||||
/* Before Nehalem _mm_loadu_si128 could be very slow, this trick is a little
|
||||
* faster. Once enough people are on architectures where _mm_loadu_si128 is
|
||||
* fast we can migrate to it.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE __m128i loadUnaligned128(const __m128i *aSource)
|
||||
{
|
||||
// Yes! We use uninitialized memory here, we'll overwrite it though!
|
||||
__m128 res = _mm_loadl_pi(_mm_set1_ps(0), (const __m64*)aSource);
|
||||
return _mm_castps_si128(_mm_loadh_pi(res, ((const __m64*)(aSource)) + 1));
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uint32_t Avg2x2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
uint32_t sum = a ^ b ^ c;
|
||||
uint32_t carry = (a & b) | (a & c) | (b & c);
|
||||
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
// Not having a byte based average instruction means we should mask to avoid
|
||||
// underflow.
|
||||
sum = (((sum ^ d) & mask) >> 1) + (sum & d);
|
||||
|
||||
return (((sum ^ carry) & mask) >> 1) + (sum & carry);
|
||||
}
|
||||
|
||||
// Simple 2 pixel average version of the function above.
|
||||
MOZ_ALWAYS_INLINE uint32_t Avg2(uint32_t a, uint32_t b)
|
||||
{
|
||||
uint32_t sum = a ^ b;
|
||||
uint32_t carry = (a & b);
|
||||
|
||||
uint32_t mask = 0xfefefefe;
|
||||
|
||||
return ((sum & mask) >> 1) + carry;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImage2D_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
const int Bpp = 4;
|
||||
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
|
||||
!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = _mm_load_si128(upperRow);
|
||||
__m128i b = _mm_load_si128(upperRow + 1);
|
||||
__m128i c = _mm_load_si128(lowerRow);
|
||||
__m128i d = _mm_load_si128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = _mm_load_si128(upperRow);
|
||||
__m128i b = _mm_load_si128(upperRow + 1);
|
||||
__m128i c = loadUnaligned128(lowerRow);
|
||||
__m128i d = loadUnaligned128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)upperRow + 1);
|
||||
__m128i c = _mm_load_si128((__m128i*)lowerRow);
|
||||
__m128i d = _mm_load_si128((__m128i*)lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i *upperRow = (__m128i*)(aSource + (y * aSourceStride + x * Bpp));
|
||||
__m128i *lowerRow = (__m128i*)(aSource + ((y + 1) * aSourceStride + x * Bpp));
|
||||
|
||||
__m128i a = loadUnaligned128(upperRow);
|
||||
__m128i b = loadUnaligned128(upperRow + 1);
|
||||
__m128i c = loadUnaligned128(lowerRow);
|
||||
__m128i d = loadUnaligned128(lowerRow + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x2(&a, &b, &c, &d);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle. We use a 2x2 'simd' implementation for this.
|
||||
//
|
||||
// Potentially we only have to do this in the last row since overflowing
|
||||
// 8 pixels in an earlier row would appear to be harmless as it doesn't
|
||||
// touch invalid memory. Even when reading and writing to the same surface.
|
||||
// in practice we only do this when doing an additional downscale pass, and
|
||||
// in this situation we have unused stride to write into harmlessly.
|
||||
// I do not believe the additional code complexity would be worth it though.
|
||||
for (; x < aSourceSize.width; x += 2) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * Bpp);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * Bpp);
|
||||
|
||||
*unalignedStorage++ = Avg2x2(*(uint32_t*)upperRow, *((uint32_t*)upperRow + 1),
|
||||
*(uint32_t*)lowerRow, *((uint32_t*)lowerRow + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageVertical_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y += 2) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y / 2) * aDestStride);
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16) &&
|
||||
!(uintptr_t(aSource + ((y + 1) * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = _mm_load_si128((__m128i*)upperRow);
|
||||
__m128i b = _mm_load_si128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
// This line doesn't align well.
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = _mm_load_si128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = _mm_load_si128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 3); x += 4) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
__m128i a = loadUnaligned128((__m128i*)upperRow);
|
||||
__m128i b = loadUnaligned128((__m128i*)lowerRow);
|
||||
|
||||
*storage++ = avg_sse2_4x2_4x1(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle.
|
||||
//
|
||||
// Similar overflow considerations are valid as in the previous function.
|
||||
for (; x < aSourceSize.width; x++) {
|
||||
uint8_t *upperRow = aSource + (y * aSourceStride + x * 4);
|
||||
uint8_t *lowerRow = aSource + ((y + 1) * aSourceStride + x * 4);
|
||||
|
||||
*unalignedStorage++ = Avg2(*(uint32_t*)upperRow, *(uint32_t*)lowerRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageHalfScaler::HalfImageHorizontal_SSE2(uint8_t *aSource, int32_t aSourceStride,
|
||||
const IntSize &aSourceSize, uint8_t *aDest,
|
||||
uint32_t aDestStride)
|
||||
{
|
||||
for (int y = 0; y < aSourceSize.height; y++) {
|
||||
__m128i *storage = (__m128i*)(aDest + (y * aDestStride));
|
||||
int x = 0;
|
||||
// Run a loop depending on alignment.
|
||||
if (!(uintptr_t(aSource + (y * aSourceStride)) % 16)) {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
__m128i a = _mm_load_si128(pixels);
|
||||
__m128i b = _mm_load_si128(pixels + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x1_4x1(a, b);
|
||||
}
|
||||
} else {
|
||||
for (; x < (aSourceSize.width - 7); x += 8) {
|
||||
__m128i* pixels = (__m128i*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
__m128i a = loadUnaligned128(pixels);
|
||||
__m128i b = loadUnaligned128(pixels + 1);
|
||||
|
||||
*storage++ = avg_sse2_8x1_4x1(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *unalignedStorage = (uint32_t*)storage;
|
||||
// Take care of the final pixels, we know there's an even number of pixels
|
||||
// in the source rectangle.
|
||||
//
|
||||
// Similar overflow considerations are valid as in the previous function.
|
||||
for (; x < aSourceSize.width; x += 2) {
|
||||
uint32_t *pixels = (uint32_t*)(aSource + (y * aSourceStride + x * 4));
|
||||
|
||||
*unalignedStorage++ = Avg2(*pixels, *(pixels + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -74,6 +74,7 @@ CPPSRCS = \
|
|||
Blur.cpp \
|
||||
ScaledFontBase.cpp \
|
||||
DrawTargetDual.cpp \
|
||||
ImageScaling.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
|
@ -117,6 +118,12 @@ ifdef MOZ_DEBUG
|
|||
DEFINES += -DGFX_LOG_DEBUG -DGFX_LOG_WARNING
|
||||
endif
|
||||
|
||||
# Are we targeting x86 or x64? If so, build SSE2 files.
|
||||
ifneq (,$(INTEL_ARCHITECTURE))
|
||||
CPPSRCS += ImageScalingSSE2.cpp
|
||||
DEFINES += -DUSE_SSE2
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
CPPSRCS += \
|
||||
DrawTargetD2D.cpp \
|
||||
|
@ -144,6 +151,18 @@ endif
|
|||
#EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL -framework QuickTime -framework AppKit -framework QuartzCore
|
||||
#endif
|
||||
|
||||
# The file uses SSE2 intrinsics, so it needs special compile flags on some
|
||||
# compilers.
|
||||
ifneq (,$(INTEL_ARCHITECTURE))
|
||||
ifdef GNU_CC
|
||||
ImageScalingSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
|
||||
endif
|
||||
|
||||
ifdef SOLARIS_SUNPRO_CXX
|
||||
ImageScalingSSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USE_SSE2;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_DEBUG;GFX_LOG_WARNING;MFBT_STAND_ALONE;XP_WIN</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
|
@ -80,6 +80,7 @@
|
|||
<ClInclude Include="DrawTargetDual.h" />
|
||||
<ClInclude Include="GradientStopsD2D.h" />
|
||||
<ClInclude Include="HelpersD2D.h" />
|
||||
<ClInclude Include="ImageScaling.h" />
|
||||
<ClInclude Include="Logging.h" />
|
||||
<ClInclude Include="Matrix.h" />
|
||||
<ClInclude Include="PathD2D.h" />
|
||||
|
@ -96,6 +97,8 @@
|
|||
<ClCompile Include="DrawTargetD2D.cpp" />
|
||||
<ClCompile Include="DrawTargetDual.cpp" />
|
||||
<ClCompile Include="Factory.cpp" />
|
||||
<ClCompile Include="ImageScaling.cpp" />
|
||||
<ClCompile Include="ImageScalingSSE2.cpp" />
|
||||
<ClCompile Include="Matrix.cpp" />
|
||||
<ClCompile Include="PathD2D.cpp" />
|
||||
<ClCompile Include="ScaledFontDWrite.cpp" />
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "SanityChecks.h"
|
||||
#include "TestPoint.h"
|
||||
#include "TestScaling.h"
|
||||
#ifdef WIN32
|
||||
#include "TestDrawTargetD2D.h"
|
||||
#endif
|
||||
|
@ -61,7 +62,8 @@ main()
|
|||
#ifdef WIN32
|
||||
{ new TestDrawTargetD2D(), "DrawTarget (D2D)" },
|
||||
#endif
|
||||
{ new TestPoint(), "Point Tests" }
|
||||
{ new TestPoint(), "Point Tests" },
|
||||
{ new TestScaling(), "Scaling Tests" }
|
||||
};
|
||||
|
||||
int totalFailures = 0;
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TestBase.h"
|
||||
|
||||
class TestPoint : public TestBase
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*
|
||||
* 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 "TestScaling.h"
|
||||
|
||||
#include "ImageScaling.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
TestScaling::TestScaling()
|
||||
{
|
||||
REGISTER_TEST(TestScaling, BasicHalfScale);
|
||||
REGISTER_TEST(TestScaling, DoubleHalfScale);
|
||||
REGISTER_TEST(TestScaling, UnevenHalfScale);
|
||||
REGISTER_TEST(TestScaling, OddStrideHalfScale);
|
||||
REGISTER_TEST(TestScaling, VerticalHalfScale);
|
||||
REGISTER_TEST(TestScaling, HorizontalHalfScale);
|
||||
REGISTER_TEST(TestScaling, MixedHalfScale);
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::BasicHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(500 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(500, 500));
|
||||
|
||||
scaler.ScaleForSize(IntSize(220, 240));
|
||||
|
||||
VERIFY(scaler.GetSize().width == 250);
|
||||
VERIFY(scaler.GetSize().height == 250);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 250; y++) {
|
||||
for (int x = 0; x < 250; x++) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::DoubleHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(500 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(500, 500));
|
||||
|
||||
scaler.ScaleForSize(IntSize(120, 110));
|
||||
VERIFY(scaler.GetSize().width == 125);
|
||||
VERIFY(scaler.GetSize().height == 125);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 125; y++) {
|
||||
for (int x = 0; x < 125; x++) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::UnevenHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
// Use a 16-byte aligned stride still, we test none-aligned strides
|
||||
// separately.
|
||||
data.resize(499 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
if (x < 498) {
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
}
|
||||
if (y < 498) {
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
if (x < 498) {
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(499, 499));
|
||||
|
||||
scaler.ScaleForSize(IntSize(220, 220));
|
||||
VERIFY(scaler.GetSize().width == 249);
|
||||
VERIFY(scaler.GetSize().height == 249);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 249; y++) {
|
||||
for (int x = 0; x < 249; x++) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::OddStrideHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
// Use a 4-byte aligned stride to test if that doesn't cause any issues.
|
||||
data.resize(499 * 499 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 499 + x] = 0xff00ff00;
|
||||
if (x < 498) {
|
||||
pixels[y * 499 + x + 1] = 0xff00ffff;
|
||||
}
|
||||
if (y < 498) {
|
||||
pixels[(y + 1) * 499 + x] = 0xff000000;
|
||||
if (x < 498) {
|
||||
pixels[(y + 1) * 499 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 499 * 4, IntSize(499, 499));
|
||||
|
||||
scaler.ScaleForSize(IntSize(220, 220));
|
||||
VERIFY(scaler.GetSize().width == 249);
|
||||
VERIFY(scaler.GetSize().height == 249);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 249; y++) {
|
||||
for (int x = 0; x < 249; x++) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f7f);
|
||||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
TestScaling::VerticalHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(500 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(500, 500));
|
||||
|
||||
scaler.ScaleForSize(IntSize(400, 240));
|
||||
VERIFY(scaler.GetSize().width == 500);
|
||||
VERIFY(scaler.GetSize().height == 250);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 250; y++) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff007f00);
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x + 1] == 0xff007fff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::HorizontalHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(500 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(500, 500));
|
||||
|
||||
scaler.ScaleForSize(IntSize(240, 400));
|
||||
VERIFY(scaler.GetSize().width == 250);
|
||||
VERIFY(scaler.GetSize().height == 500);
|
||||
|
||||
pixels = (uint32_t*)scaler.GetScaledData();
|
||||
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 250; x++) {
|
||||
VERIFY(pixels[y * (scaler.GetStride() / 4) + x] == 0xff00ff7f);
|
||||
VERIFY(pixels[(y + 1) * (scaler.GetStride() / 4) + x] == 0xff00007f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TestScaling::MixedHalfScale()
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
data.resize(500 * 500 * 4);
|
||||
|
||||
uint32_t *pixels = (uint32_t*)data.data();
|
||||
for (int y = 0; y < 500; y += 2) {
|
||||
for (int x = 0; x < 500; x += 2) {
|
||||
pixels[y * 500 + x] = 0xff00ff00;
|
||||
pixels[y * 500 + x + 1] = 0xff00ffff;
|
||||
pixels[(y + 1) * 500 + x] = 0xff000000;
|
||||
pixels[(y + 1) * 500 + x + 1] = 0xff0000ff;
|
||||
}
|
||||
}
|
||||
ImageHalfScaler scaler(data.data(), 500 * 4, IntSize(500, 500));
|
||||
|
||||
scaler.ScaleForSize(IntSize(120, 240));
|
||||
VERIFY(scaler.GetSize().width == 125);
|
||||
VERIFY(scaler.GetSize().height == 250);
|
||||
scaler.ScaleForSize(IntSize(240, 120));
|
||||
VERIFY(scaler.GetSize().width == 250);
|
||||
VERIFY(scaler.GetSize().height == 125);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ***** 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) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TestBase.h"
|
||||
|
||||
class TestScaling : public TestBase
|
||||
{
|
||||
public:
|
||||
TestScaling();
|
||||
|
||||
void BasicHalfScale();
|
||||
void DoubleHalfScale();
|
||||
void UnevenHalfScale();
|
||||
void OddStrideHalfScale();
|
||||
void VerticalHalfScale();
|
||||
void HorizontalHalfScale();
|
||||
void MixedHalfScale();
|
||||
};
|
|
@ -77,6 +77,7 @@
|
|||
<ClCompile Include="TestDrawTargetBase.cpp" />
|
||||
<ClCompile Include="TestDrawTargetD2D.cpp" />
|
||||
<ClCompile Include="TestPoint.cpp" />
|
||||
<ClCompile Include="TestScaling.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="TestDrawTargetBase.h" />
|
||||
|
@ -84,6 +85,7 @@
|
|||
<ClInclude Include="TestBase.h" />
|
||||
<ClInclude Include="TestDrawTargetD2D.h" />
|
||||
<ClInclude Include="TestPoint.h" />
|
||||
<ClInclude Include="TestScaling.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
Загрузка…
Ссылка в новой задаче