b=using opacity with windows native widgets causes bogus rendering/lack of rendering; r=stuart

git-svn-id: svn://10.0.0.236/trunk@221253 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
vladimir%pobox.com 2007-03-03 00:18:34 +00:00
parent 9decb54e91
commit 76df446d7a
11 changed files with 887 additions and 145 deletions

View File

@ -11,6 +11,7 @@ MODULE = thebes
REQUIRES = cairo
EXPORTS = gfxASurface.h \
gfxAlphaRecovery.h \
gfxColor.h \
gfxContext.h \
gfxFont.h \
@ -35,7 +36,9 @@ endif
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
EXPORTS += gfxWindowsFonts.h \
gfxWindowsPlatform.h \
gfxWindowsSurface.h
gfxWindowsSurface.h \
gfxWindowsNativeDrawing.h \
$(NULL)
EXPORTS += gfxPDFSurface.h
ifdef MOZ_ENABLE_GLITZ

View File

@ -0,0 +1,55 @@
/* -*- 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 Thebes gfx.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.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 _GFXALPHARECOVERY_H_
#define _GFXALPHARECOVERY_H_
#include "gfxContext.h"
#include "gfxImageSurface.h"
class THEBES_API gfxAlphaRecovery {
public:
/* Given two RGB24 surfaces with the same rendering, one on a black
* background and the other on white, return a new surface
* that contains the contents with recovered alpha.
*/
static already_AddRefed<gfxImageSurface> RecoverAlpha (gfxImageSurface *blackSurface,
gfxImageSurface *whiteSurface,
gfxIntSize dimensions);
};
#endif /* _GFXALPHARECOVERY_H_ */

View File

@ -183,10 +183,61 @@ public:
(xy != 0.0) || (yx != 0.0));
}
/**
* Returns true if the matrix has any transform other
* than a translation or a -1 y scale (y axis flip)
*/
bool HasNonTranslationOrFlip() const {
return ((xx != 1.0) || ((yy != 1.0) && (yy != -1.0)) ||
(xy != 0.0) || (yx != 0.0));
}
/**
* Returns true if the matrix has any transform other
* than a translation or scale; this is, if there is
* no rotation.
*/
bool HasNonAxisAlignedTransform() const {
return ((xy != 0.0) || (yx != 0.0));
}
/**
* Computes the determinant of this matrix.
*/
double Determinant() const {
return xx*yy - yx*xy;
}
/* Computes the scale factors of this matrix; that is,
* the amounts each basis vector is scaled by.
* The xMajor parameter indicates if the larger scale is
* to be assumed to be in the X direction or not.
*/
gfxSize ScaleFactors(PRBool xMajor) const {
double det = Determinant();
if (det == 0.0)
return gfxSize(0.0, 0.0);
gfxSize sz((xMajor != 0 ? 1.0 : 0.0),
(xMajor != 0 ? 0.0 : 1.0));
sz = Transform(sz);
double major = sqrt(sz.width * sz.width + sz.height * sz.height);
double minor = 0.0;
// ignore mirroring
if (det < 0.0)
det = - det;
if (major)
minor = det / major;
if (xMajor)
return gfxSize(major, minor);
return gfxSize(minor, major);
}
};
#endif /* GFX_MATRIX_H */

View File

@ -63,8 +63,9 @@ public:
cairo_pattern_t *CairoPattern();
void AddColorStop(gfxFloat offset, const gfxRGBA& c);
void SetMatrix(const gfxMatrix& matrix);
gfxMatrix CurrentMatrix() const;
gfxMatrix GetMatrix() const;
enum GraphicsExtend {
EXTEND_NONE,
@ -83,6 +84,8 @@ public:
/* returns TRUE if it succeeded */
PRBool GetSolidColor(gfxRGBA& aColor);
already_AddRefed<gfxASurface> GetSurface();
protected:
cairo_pattern_t *mPattern;
};

View File

@ -0,0 +1,158 @@
/* -*- 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 Thebes gfx.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.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 _GFXWINDOWSNATIVEDRAWING_H_
#define _GFXWINDOWSNATIVEDRAWING_H_
#include <windows.h>
#include "gfxContext.h"
#include "gfxWindowsSurface.h"
class THEBES_API gfxWindowsNativeDrawing {
public:
/* Flags for notifying this class what kind of operations the native
* drawing supports
*/
enum {
/* Whether the native drawing can draw to a surface of content COLOR_ALPHA */
CAN_DRAW_TO_COLOR_ALPHA = 1 << 0,
CANNOT_DRAW_TO_COLOR_ALPHA = 0 << 0,
/* Whether the native drawing can be scaled using SetWorldTransform */
CAN_AXIS_ALIGNED_SCALE = 1 << 1,
CANNOT_AXIS_ALIGNED_SCALE = 0 << 1,
/* Whether the native drawing can be both scaled and rotated arbitrarily using SetWorldTransform */
CAN_COMPLEX_TRANSFORM = 1 << 2,
CANNOT_COMPLEX_TRANSFORM = 0 << 2,
/* If we have to do transforms with cairo, should we use nearest-neighbour filtering? */
DO_NEAREST_NEIGHBOR_FILTERING = 1 << 3,
DO_BILINEAR_FILTERING = 0 << 3
};
/* Create native win32 drawing for a rectangle bounded by
* nativeRect.
*
* This class assumes that native drawing can take place only if
* the destination surface has a content type of COLOR (that is,
* RGB24), and that the transformation matrix consists of only a
* translation (in which case the coordinates are munged directly)
* or a translation and scale (in which case SetWorldTransform is used).
*
* If the destination is of a non-win32 surface type, a win32
* surface of content COLOR_ALPHA, or if there is a complex
* transform (i.e., one with rotation) set, then the native drawing
* code will fall back to alpha recovery, but will still take advantage
* of native axis-aligned scaling.
*
* Typical usage looks like:
*
* gfxWindowsNativeDrawing nativeDraw(ctx, destGfxRect);
* do {
* HDC dc = nativeDraw.BeginNativeDrawing();
*
* RECT winRect;
* nativeDraw.TransformToNativeRect(rect, winRect);
*
* ... call win32 operations on HDC to draw to winRect ...
*
* nativeDraw.EndNativeDrawing();
* } while (nativeDraw.ShouldRenderAgain());
* nativeDraw.PaintToContext();
*/
gfxWindowsNativeDrawing(gfxContext *ctx,
const gfxRect& nativeRect,
PRUint32 nativeDrawFlags = CANNOT_DRAW_TO_COLOR_ALPHA |
CANNOT_AXIS_ALIGNED_SCALE |
CANNOT_COMPLEX_TRANSFORM |
DO_BILINEAR_FILTERING);
/* Returns a HDC which may be used for native drawing. This HDC is valid
* until EndNativeDrawing is called; if it is used for drawing after that time,
* the result is undefined. */
HDC BeginNativeDrawing();
/* Transform the native rect into something valid for rendering
* to the HDC. This may or may not change RECT, depending on
* whether SetWorldTransform is used or not. */
void TransformToNativeRect(const gfxRect& r, RECT& rout);
/* Marks the end of native drawing */
void EndNativeDrawing();
/* Returns PR_TRUE if the native drawing should be executed again */
PRBool ShouldRenderAgain();
/* Places the result to the context, if necessary */
void PaintToContext();
private:
nsRefPtr<gfxContext> mContext;
gfxRect mNativeRect;
PRUint32 mNativeDrawFlags;
// what state the rendering is in
PRUint8 mRenderState;
gfxPoint mDeviceOffset;
nsRefPtr<gfxPattern> mBlackPattern, mWhitePattern;
enum TransformType {
TRANSLATION_ONLY,
AXIS_ALIGNED_SCALE,
COMPLEX
};
TransformType mTransformType;
gfxPoint mTranslation;
gfxSize mScale;
XFORM mWorldTransform;
// saved state
nsRefPtr<gfxWindowsSurface> mWinSurface, mBlackSurface, mWhiteSurface;
HDC mDC;
XFORM mOldWorldTransform;
POINT mOrigViewportOrigin;
gfxIntSize mTempSurfaceSize;
};
#endif

View File

@ -22,6 +22,7 @@ REQUIRES = \
CPPSRCS = \
gfxASurface.cpp \
gfxAlphaRecovery.cpp \
gfxContext.cpp \
gfxImageSurface.cpp \
gfxFont.cpp \
@ -55,6 +56,7 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
CPPSRCS += gfxWindowsFonts.cpp \
gfxWindowsPlatform.cpp \
gfxWindowsSurface.cpp \
gfxWindowsNativeDrawing.cpp \
nsUnicodeRange.cpp \
$(NULL)
CPPSRCS += gfxPDFSurface.cpp

View File

@ -0,0 +1,172 @@
/* -*- 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 Thebes gfx.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.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 "gfxAlphaRecovery.h"
#include "gfxImageSurface.h"
struct gfxAlphaRecoveryResult {
gfxAlphaRecoveryResult()
: uniformColor(PR_FALSE),
uniformAlpha(PR_FALSE)
{ }
PRBool uniformColor;
PRBool uniformAlpha;
gfxFloat alpha;
gfxFloat r, g, b;
};
static void _compute_alpha_values (unsigned int *black_data,
unsigned int *white_data,
gfxIntSize dimensions,
gfxAlphaRecoveryResult *result);
already_AddRefed<gfxImageSurface>
gfxAlphaRecovery::RecoverAlpha (gfxImageSurface *blackSurf,
gfxImageSurface *whiteSurf,
gfxIntSize dimensions)
{
nsRefPtr<gfxImageSurface> resultSurf;
resultSurf = new gfxImageSurface(dimensions, gfxASurface::ImageFormatARGB32);
// copy blackSurf into resultSurf
nsRefPtr<gfxContext> ctx = new gfxContext(resultSurf);
ctx->SetSource(blackSurf);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->Paint();
ctx = nsnull;
gfxAlphaRecoveryResult result;
_compute_alpha_values ((unsigned int*) resultSurf->Data(),
(unsigned int*) whiteSurf->Data(),
dimensions,
&result);
// XX use result, maybe return pattern, etc.
NS_ADDREF(resultSurf.get());
return resultSurf;
}
/** from cairo-xlib-utils.c, modified */
/**
* Given the RGB data for two image surfaces, one a source image composited
* with OVER onto a black background, and one a source image composited with
* OVER onto a white background, reconstruct the original image data into
* black_data.
*
* Consider a single color channel and a given pixel. Suppose the original
* premultiplied color value was C and the alpha value was A. Let the final
* on-black color be B and the final on-white color be W. All values range
* over 0-255.
* Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get
* A=255 - (W - C). Therefore it suffices to leave the black_data color
* data alone and set the alpha values using that simple formula. It shouldn't
* matter what color channel we pick for the alpha computation, but we'll
* pick green because if we went through a color channel downsample the green
* bits are likely to be the most accurate.
*/
#define SET_ALPHA(v, a) (((v) & ~(0xFF << 24)) | ((a) << 24))
#define GREEN_OF(v) (((v) >> 8) & 0xFF)
static void
_compute_alpha_values (unsigned int *black_data,
unsigned int *white_data,
gfxIntSize dimensions,
gfxAlphaRecoveryResult *result)
{
int num_pixels = dimensions.width * dimensions.height;
int i;
unsigned int first;
unsigned int deltas = 0;
unsigned char first_alpha;
if (num_pixels == 0) {
if (result) {
result->uniformAlpha = PR_TRUE;
result->uniformColor = PR_TRUE;
/* whatever we put here will be true */
result->alpha = 1.0;
result->r = result->g = result->b = 0.0;
}
return;
}
first_alpha = 255 - (GREEN_OF(*white_data) - GREEN_OF(*black_data));
/* set the alpha value of 'first' */
first = SET_ALPHA(*black_data, first_alpha);
for (i = 0; i < num_pixels; ++i) {
unsigned int black = *black_data;
unsigned int white = *white_data;
unsigned char pixel_alpha = 255 - (GREEN_OF(white) - GREEN_OF(black));
black = SET_ALPHA(black, pixel_alpha);
*black_data = black;
deltas |= (first ^ black);
black_data++;
white_data++;
}
if (result) {
result->uniformAlpha = (deltas >> 24) == 0;
if (result->uniformAlpha) {
result->alpha = first_alpha/255.0;
/* we only set uniformColor when the alpha is already uniform.
it's only useful in that case ... and if the alpha was nonuniform
then computing whether the color is uniform would require unpremultiplying
every pixel */
result->uniformColor = (deltas & ~(0xFF << 24)) == 0;
if (result->uniformColor) {
if (first_alpha == 0) {
/* can't unpremultiply, this is OK */
result->r = result->g = result->b = 0.0;
} else {
double d_first_alpha = first_alpha;
result->r = (first & 0xFF)/d_first_alpha;
result->g = ((first >> 8) & 0xFF)/d_first_alpha;
result->b = ((first >> 16) & 0xFF)/d_first_alpha;
}
}
}
}
}

View File

@ -96,7 +96,7 @@ gfxPattern::SetMatrix(const gfxMatrix& matrix)
}
gfxMatrix
gfxPattern::CurrentMatrix() const
gfxPattern::GetMatrix() const
{
cairo_matrix_t mat;
cairo_pattern_get_matrix(mPattern, &mat);
@ -136,3 +136,15 @@ gfxPattern::GetSolidColor(gfxRGBA& aColor)
&aColor.b,
&aColor.a) == CAIRO_STATUS_SUCCESS;
}
already_AddRefed<gfxASurface>
gfxPattern::GetSurface()
{
cairo_surface_t *surf = nsnull;
if (cairo_pattern_get_surface (mPattern, &surf) != CAIRO_STATUS_SUCCESS)
return nsnull;
return gfxASurface::Wrap(surf);
}

View File

@ -0,0 +1,310 @@
/* -*- 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 Thebes gfx.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.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 <windows.h>
#include "nsMathUtils.h"
#include "gfxWindowsNativeDrawing.h"
#include "gfxWindowsSurface.h"
#include "gfxAlphaRecovery.h"
enum {
RENDER_STATE_INIT,
RENDER_STATE_NATIVE_DRAWING,
RENDER_STATE_NATIVE_DRAWING_DONE,
RENDER_STATE_ALPHA_RECOVERY_BLACK,
RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE,
RENDER_STATE_ALPHA_RECOVERY_WHITE,
RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE,
RENDER_STATE_DONE
};
gfxWindowsNativeDrawing::gfxWindowsNativeDrawing(gfxContext* ctx,
const gfxRect& nativeRect,
PRUint32 nativeDrawFlags)
: mContext(ctx), mNativeRect(nativeRect), mNativeDrawFlags(nativeDrawFlags), mRenderState(RENDER_STATE_INIT)
{
}
HDC
gfxWindowsNativeDrawing::BeginNativeDrawing()
{
if (mRenderState == RENDER_STATE_INIT) {
nsRefPtr<gfxASurface> surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y);
gfxMatrix m = mContext->CurrentMatrix();
if (!m.HasNonTranslation())
mTransformType = TRANSLATION_ONLY;
else if (m.HasNonAxisAlignedTransform())
mTransformType = COMPLEX;
else
mTransformType = AXIS_ALIGNED_SCALE;
// if this is a native win32 surface, we don't have to
// redirect rendering to our own HDC; in some cases,
// we may be able to use the HDC from the surface directly.
if (surf->GetType() == gfxASurface::SurfaceTypeWin32 &&
(surf->GetContentType() == gfxASurface::CONTENT_COLOR ||
(surf->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA &&
(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA))))
{
if (mTransformType == TRANSLATION_ONLY) {
mRenderState = RENDER_STATE_NATIVE_DRAWING;
mTranslation = m.GetTranslation();
mWinSurface = NS_STATIC_CAST(gfxWindowsSurface*, NS_STATIC_CAST(gfxASurface*, surf.get()));
} else if (((mTransformType == AXIS_ALIGNED_SCALE)
&& (mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) ||
(mNativeDrawFlags & CAN_COMPLEX_TRANSFORM))
{
mWorldTransform.eM11 = (FLOAT) m.xx;
mWorldTransform.eM12 = (FLOAT) m.yx;
mWorldTransform.eM21 = (FLOAT) m.xy;
mWorldTransform.eM22 = (FLOAT) m.yy;
mWorldTransform.eDx = (FLOAT) m.x0;
mWorldTransform.eDy = (FLOAT) m.y0;
mRenderState = RENDER_STATE_NATIVE_DRAWING;
mWinSurface = NS_STATIC_CAST(gfxWindowsSurface*, NS_STATIC_CAST(gfxASurface*, surf.get()));
}
}
// If we couldn't do native drawing, then we have to do two-buffer drawing
// and do alpha recovery
if (mRenderState == RENDER_STATE_INIT) {
mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK;
// we only do the scale bit if we can do an axis aligned
// scale; otherwise we scale (if necessary) after
// rendering with cairo. Note that if we're doing alpha recovery,
// we cannot do a full complex transform with win32 (I mean, we could, but
// it would require more code that's not here.)
if (mTransformType == TRANSLATION_ONLY || !(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE)) {
mScale = gfxSize(1.0, 1.0);
mTempSurfaceSize.width = (PRInt32) NS_ceil(mNativeRect.size.width);
mTempSurfaceSize.height = (PRInt32) NS_ceil(mNativeRect.size.height);
} else {
// figure out the scale factors
mScale = m.ScaleFactors(PR_TRUE);
mWorldTransform.eM11 = (FLOAT) mScale.width;
mWorldTransform.eM12 = 0.0f;
mWorldTransform.eM21 = 0.0f;
mWorldTransform.eM22 = (FLOAT) mScale.height;
mWorldTransform.eDx = 0.0f;
mWorldTransform.eDy = 0.0f;
mTempSurfaceSize.width = (PRInt32) NS_ceil(mNativeRect.size.width * mScale.width);
mTempSurfaceSize.height = (PRInt32) NS_ceil(mNativeRect.size.height * mScale.height);
}
}
}
if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
// we can just do native drawing directly to the context's surface
// Need to force the clip to be set
mContext->UpdateSurfaceClip();
// grab the DC
mDC = mWinSurface->GetDC();
// do we need to use SetWorldTransform?
if (mTransformType != TRANSLATION_ONLY) {
SetGraphicsMode(mDC, GM_ADVANCED);
GetWorldTransform(mDC, &mOldWorldTransform);
SetWorldTransform(mDC, &mWorldTransform);
}
GetViewportOrgEx(mDC, &mOrigViewportOrigin);
SetViewportOrgEx(mDC,
mOrigViewportOrigin.x + (int)mDeviceOffset.x,
mOrigViewportOrigin.y + (int)mDeviceOffset.y,
NULL);
return mDC;
} else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK ||
mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE)
{
// we're going to use mWinSurface to create our temporary surface here
// get us a RGB24 DIB; DIB is important, because
// we can later call GetImageSurface on it.
mWinSurface = new gfxWindowsSurface(mTempSurfaceSize);
mDC = mWinSurface->GetDC();
RECT r = { 0, 0, mTempSurfaceSize.width, mTempSurfaceSize.height };
if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK)
FillRect(mDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));
else
FillRect(mDC, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
if ((mTransformType != TRANSLATION_ONLY) &&
(mNativeDrawFlags & CAN_AXIS_ALIGNED_SCALE))
{
SetGraphicsMode(mDC, GM_ADVANCED);
SetWorldTransform(mDC, &mWorldTransform);
}
return mDC;
} else {
NS_ERROR("Bogus render state!");
return nsnull;
}
}
PRBool
gfxWindowsNativeDrawing::ShouldRenderAgain()
{
switch (mRenderState) {
case RENDER_STATE_NATIVE_DRAWING_DONE:
return PR_FALSE;
case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
return PR_TRUE;
case RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE:
return PR_FALSE;
default:
NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::ShouldRenderAgain");
break;
}
return PR_FALSE;
}
void
gfxWindowsNativeDrawing::EndNativeDrawing()
{
if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
// we drew directly to the HDC in the context; undo our changes
SetViewportOrgEx(mDC, mOrigViewportOrigin.x, mOrigViewportOrigin.y, NULL);
if (mTransformType != TRANSLATION_ONLY)
SetWorldTransform(mDC, &mOldWorldTransform);
mWinSurface->MarkDirty();
mRenderState = RENDER_STATE_NATIVE_DRAWING_DONE;
} else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_BLACK) {
mBlackSurface = mWinSurface;
mWinSurface = nsnull;
mRenderState = RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE;
} else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE) {
mWhiteSurface = mWinSurface;
mWinSurface = nsnull;
mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE;
} else {
NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::EndNativeDrawing");
}
}
void
gfxWindowsNativeDrawing::PaintToContext()
{
if (mRenderState == RENDER_STATE_NATIVE_DRAWING_DONE) {
// nothing to do, it already went to the context
mRenderState = RENDER_STATE_DONE;
} else if (mRenderState == RENDER_STATE_ALPHA_RECOVERY_WHITE_DONE) {
nsRefPtr<gfxImageSurface> black = mBlackSurface->GetImageSurface();
nsRefPtr<gfxImageSurface> white = mWhiteSurface->GetImageSurface();
nsRefPtr<gfxImageSurface> alphaSurface =
gfxAlphaRecovery::RecoverAlpha(black, white, mTempSurfaceSize);
mContext->Save();
mContext->MoveTo(mNativeRect.pos);
mContext->NewPath();
mContext->Rectangle(gfxRect(gfxPoint(0.0, 0.0), mNativeRect.size));
nsRefPtr<gfxPattern> pat = new gfxPattern(alphaSurface);
gfxMatrix m;
m.Scale(mScale.width, mScale.height);
pat->SetMatrix(m);
if (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING)
pat->SetFilter(0);
mContext->SetPattern(pat);
mContext->Fill();
mContext->Restore();
mRenderState = RENDER_STATE_DONE;
} else {
NS_ERROR("Invalid RenderState in gfxWindowsNativeDrawing::PaintToContext");
}
}
void
gfxWindowsNativeDrawing::TransformToNativeRect(const gfxRect& r,
RECT& rout)
{
/* If we're doing native drawing, then we're still in the coordinate space
* of the context; otherwise, we're in our own little world,
* relative to the passed-in nativeRect.
*/
if (mRenderState == RENDER_STATE_NATIVE_DRAWING) {
if (mTransformType == TRANSLATION_ONLY) {
rout.left = (LONG) (r.pos.x + NS_round(mTranslation.x));
rout.right = (LONG) (rout.left + r.size.width);
rout.top = (LONG) (r.pos.y + NS_round(mTranslation.y));
rout.bottom = (LONG) (rout.top + r.size.height);
} else {
rout.left = (LONG) r.pos.x;
rout.right = (LONG) (r.pos.x + r.size.width);
rout.top = (LONG) r.pos.y;
rout.bottom = (LONG) (r.pos.y + r.size.height);
}
} else {
rout.left = (LONG) (r.pos.x - NS_round(mNativeRect.pos.x));
rout.right = (LONG) (rout.left + r.size.width);
rout.top = (LONG) (r.pos.y - NS_round(mNativeRect.pos.y));
rout.bottom = (LONG) (rout.top + r.size.height);
}
}

View File

@ -59,11 +59,11 @@
#include "nsWidgetAtoms.h"
#include <malloc.h>
#ifdef MOZ_CAIRO_GFX
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "gfxWindowsSurface.h"
#endif
#include "gfxWindowsNativeDrawing.h"
/*
* The following constants are used to determine how a widget is drawn using
* Windows' Theme API. For more information on theme parts and states see
@ -821,99 +821,38 @@ nsNativeThemeWin::DrawWidgetBackground(nsIRenderingContext* aContext,
PRInt32 p2a = dc->AppUnitsPerDevPixel();
RECT widgetRect;
RECT clipRect;
nsRect tr(aRect);
nsRect cr(aClipRect);
gfxRect tr, cr;
#ifdef MOZ_CAIRO_GFX
tr.x = NSAppUnitsToIntPixels(tr.x, p2a);
tr.y = NSAppUnitsToIntPixels(tr.y, p2a);
tr.width = NSAppUnitsToIntPixels(tr.width, p2a);
tr.height = NSAppUnitsToIntPixels(tr.height, p2a);
tr.pos.x = NSAppUnitsToIntPixels(aRect.x, p2a);
tr.pos.y = NSAppUnitsToIntPixels(aRect.y, p2a);
tr.size.width = NSAppUnitsToIntPixels(aRect.width, p2a);
tr.size.height = NSAppUnitsToIntPixels(aRect.height, p2a);
cr.x = NSAppUnitsToIntPixels(cr.x, p2a);
cr.y = NSAppUnitsToIntPixels(cr.y, p2a);
cr.width = NSAppUnitsToIntPixels(cr.width, p2a);
cr.height = NSAppUnitsToIntPixels(cr.height, p2a);
cr.pos.x = NSAppUnitsToIntPixels(aClipRect.x, p2a);
cr.pos.y = NSAppUnitsToIntPixels(aClipRect.y, p2a);
cr.size.width = NSAppUnitsToIntPixels(aClipRect.width, p2a);
cr.size.height = NSAppUnitsToIntPixels(aClipRect.height, p2a);
nsRefPtr<gfxContext> ctx = (gfxContext*)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
gfxPoint offset;
nsRefPtr<gfxASurface> surf = ctx->CurrentSurface(&offset.x, &offset.y);
gfxWindowsNativeDrawing nativeDrawing(ctx, cr, GetWidgetNativeDrawingFlags(aWidgetType));
HDC hdc = NS_STATIC_CAST(gfxWindowsSurface*, NS_STATIC_CAST(gfxASurface*, surf.get()))->GetDC();
RENDER_AGAIN:
/* Need to force the clip to be set */
ctx->UpdateSurfaceClip();
HDC hdc = nativeDrawing.BeginNativeDrawing();
/* Covert the current transform to a world transform */
XFORM oldWorldTransform;
gfxMatrix m = ctx->CurrentMatrix();
if (m.HasNonTranslation()) {
GetWorldTransform(hdc, &oldWorldTransform);
SetGraphicsMode(hdc, GM_ADVANCED);
XFORM xform;
xform.eM11 = (FLOAT) m.xx;
xform.eM12 = (FLOAT) m.yx;
xform.eM21 = (FLOAT) m.xy;
xform.eM22 = (FLOAT) m.yy;
xform.eDx = (FLOAT) m.x0;
xform.eDy = (FLOAT) m.y0;
SetWorldTransform (hdc, &xform);
} else {
gfxPoint pos(m.GetTranslation());
tr.x += NSToCoordRound(pos.x);
tr.y += NSToCoordRound(pos.y);
cr.x += NSToCoordRound(pos.x);
cr.y += NSToCoordRound(pos.y);
}
nativeDrawing.TransformToNativeRect(tr, widgetRect);
nativeDrawing.TransformToNativeRect(cr, clipRect);
#if 0
{
double dm[6];
m.ToValues(&dm[0], &dm[1], &dm[2], &dm[3], &dm[4], &dm[5]);
fprintf (stderr, "xform: %f %f %f %f [%f %f]\n", dm[0], dm[1], dm[2], dm[3], dm[4], dm[5]);
fprintf (stderr, "tr: [%d %d %d %d]\ncr: [%d %d %d %d]\noff: [%f %f]\n",
tr.x, tr.y, tr.width, tr.height, cr.x, cr.y, cr.width, cr.height,
offset.x, offset.y);
fflush (stderr);
fprintf (stderr, "xform: %f %f %f %f [%f %f]\n", m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
fprintf (stderr, "tr: [%d %d %d %d]\ncr: [%d %d %d %d]\noff: [%f %f]\n",
tr.x, tr.y, tr.width, tr.height, cr.x, cr.y, cr.width, cr.height,
offset.x, offset.y);
}
#endif
/* Set the device offsets as appropriate */
POINT origViewportOrigin, origBrushOrigin;
GetViewportOrgEx(hdc, &origViewportOrigin);
SetViewportOrgEx(hdc, origViewportOrigin.x + (int)offset.x, origViewportOrigin.y + (int)offset.y, NULL);
#else /* non-MOZ_CAIRO_GFX */
nsTransform2D* transformMatrix;
aContext->GetCurrentTransform(transformMatrix);
transformMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height);
transformMatrix->TransformCoord(&cr.x,&cr.y,&cr.width,&cr.height);
HDC hdc = (HDC)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_WINDOWS_DC);
if (!hdc)
return NS_ERROR_FAILURE;
#ifndef WINCE
SetGraphicsMode(hdc, GM_ADVANCED);
#endif
#endif /* MOZ_CAIRO_GFX */
GetNativeRect(tr, widgetRect);
GetNativeRect(cr, clipRect);
#if 0
fprintf (stderr, "widget: [%d %d %d %d]\nclip: [%d %d %d %d]\n",
widgetRect.left, widgetRect.top, widgetRect.right, widgetRect.bottom,
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
fflush (stderr);
#endif
// For left edge and right edge tabs, we need to adjust the widget
// rects and clip rects so that the edges don't get drawn.
if (aWidgetType == NS_THEME_TAB_LEFT_EDGE || aWidgetType == NS_THEME_TAB_RIGHT_EDGE) {
@ -1003,14 +942,12 @@ nsNativeThemeWin::DrawWidgetBackground(nsIRenderingContext* aContext,
}
}
#ifdef MOZ_CAIRO_GFX
SetViewportOrgEx(hdc, origViewportOrigin.x, origViewportOrigin.y, NULL);
nativeDrawing.EndNativeDrawing();
if (m.HasNonTranslation())
SetWorldTransform(hdc, &oldWorldTransform);
if (nativeDrawing.ShouldRenderAgain())
goto RENDER_AGAIN;
surf->MarkDirty();
#endif
nativeDrawing.PaintToContext();
return NS_OK;
}
@ -2000,7 +1937,7 @@ nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsIRenderingContext* aCon
PRUint8 aWidgetType,
const nsRect& aRect,
const nsRect& aClipRect)
{
{
PRInt32 part, state;
PRBool focused;
nsresult rv;
@ -2012,61 +1949,27 @@ nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsIRenderingContext* aCon
aContext->GetDeviceContext(*getter_AddRefs(dc));
PRInt32 p2a = dc->AppUnitsPerDevPixel();
RECT widgetRect;
nsRect tr(aRect);
gfxRect tr, cr;
#ifdef MOZ_CAIRO_GFX
tr.x = NSAppUnitsToIntPixels(tr.x, p2a);
tr.y = NSAppUnitsToIntPixels(tr.y, p2a);
tr.width = NSAppUnitsToIntPixels(tr.width, p2a);
tr.height = NSAppUnitsToIntPixels(tr.height, p2a);
tr.pos.x = NSAppUnitsToIntPixels(aRect.x, p2a);
tr.pos.y = NSAppUnitsToIntPixels(aRect.y, p2a);
tr.size.width = NSAppUnitsToIntPixels(aRect.width, p2a);
tr.size.height = NSAppUnitsToIntPixels(aRect.height, p2a);
cr.pos.x = NSAppUnitsToIntPixels(aClipRect.x, p2a);
cr.pos.y = NSAppUnitsToIntPixels(aClipRect.y, p2a);
cr.size.width = NSAppUnitsToIntPixels(aClipRect.width, p2a);
cr.size.height = NSAppUnitsToIntPixels(aClipRect.height, p2a);
nsRefPtr<gfxContext> ctx = (gfxContext*)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT);
gfxPoint offset;
nsRefPtr<gfxASurface> surf = ctx->CurrentSurface(&offset.x, &offset.y);
HDC hdc = NS_STATIC_CAST(gfxWindowsSurface*, NS_STATIC_CAST(gfxASurface*, surf.get()))->GetDC();
gfxWindowsNativeDrawing nativeDrawing(ctx, cr, GetWidgetNativeDrawingFlags(aWidgetType));
/* Need to force the clip to be set */
ctx->UpdateSurfaceClip();
RENDER_AGAIN:
/* Covert the current transform to a world transform */
XFORM oldWorldTransform;
gfxMatrix m = ctx->CurrentMatrix();
if (m.HasNonTranslation()) {
GetWorldTransform(hdc, &oldWorldTransform);
HDC hdc = nativeDrawing.BeginNativeDrawing();
SetGraphicsMode(hdc, GM_ADVANCED);
XFORM xform;
xform.eM11 = (FLOAT) m.xx;
xform.eM12 = (FLOAT) m.yx;
xform.eM21 = (FLOAT) m.xy;
xform.eM22 = (FLOAT) m.yy;
xform.eDx = (FLOAT) m.x0;
xform.eDy = (FLOAT) m.y0;
SetWorldTransform (hdc, &xform);
} else {
gfxPoint pos(m.GetTranslation());
tr.x += NSToCoordRound(pos.x);
tr.y += NSToCoordRound(pos.y);
}
/* Set the device offsets as appropriate */
POINT origViewportOrigin;
GetViewportOrgEx(hdc, &origViewportOrigin);
SetViewportOrgEx(hdc, origViewportOrigin.x + (int)offset.x, origViewportOrigin.y + (int)offset.y, NULL);
#else /* non-MOZ_CAIRO_GFX */
nsTransform2D* transformMatrix;
aContext->GetCurrentTransform(transformMatrix);
transformMatrix->TransformCoord(&tr.x,&tr.y,&tr.width,&tr.height);
HDC hdc = (HDC)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_WINDOWS_DC);
#endif /* MOZ_CAIRO_GFX */
GetNativeRect(tr, widgetRect);
nativeDrawing.TransformToNativeRect(tr, widgetRect);
rv = NS_OK;
switch (aWidgetType) {
@ -2336,18 +2239,89 @@ nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(nsIRenderingContext* aCon
break;
}
#ifdef MOZ_CAIRO_GFX
SetViewportOrgEx(hdc, origViewportOrigin.x, origViewportOrigin.y, NULL);
nativeDrawing.EndNativeDrawing();
if (m.HasNonTranslation())
SetWorldTransform(hdc, &oldWorldTransform);
if (NS_FAILED(rv))
return rv;
surf->MarkDirty();
#endif
if (nativeDrawing.ShouldRenderAgain())
goto RENDER_AGAIN;
nativeDrawing.PaintToContext();
return rv;
}
PRUint32
nsNativeThemeWin::GetWidgetNativeDrawingFlags(PRUint8 aWidgetType)
{
switch (aWidgetType) {
case NS_THEME_BUTTON:
case NS_THEME_TEXTFIELD:
case NS_THEME_DROPDOWN:
case NS_THEME_DROPDOWN_TEXTFIELD:
return
gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
// need to check these others
case NS_THEME_SCROLLBAR_BUTTON_UP:
case NS_THEME_SCROLLBAR_BUTTON_DOWN:
case NS_THEME_SCROLLBAR_BUTTON_LEFT:
case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
case NS_THEME_SCALE_HORIZONTAL:
case NS_THEME_SCALE_VERTICAL:
case NS_THEME_SCALE_THUMB_HORIZONTAL:
case NS_THEME_SCALE_THUMB_VERTICAL:
case NS_THEME_SPINNER_UP_BUTTON:
case NS_THEME_SPINNER_DOWN_BUTTON:
case NS_THEME_LISTBOX:
case NS_THEME_TREEVIEW:
case NS_THEME_TOOLTIP:
case NS_THEME_STATUSBAR:
case NS_THEME_STATUSBAR_PANEL:
case NS_THEME_STATUSBAR_RESIZER_PANEL:
case NS_THEME_RESIZER:
case NS_THEME_PROGRESSBAR:
case NS_THEME_PROGRESSBAR_VERTICAL:
case NS_THEME_PROGRESSBAR_CHUNK:
case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
case NS_THEME_TAB:
case NS_THEME_TAB_LEFT_EDGE:
case NS_THEME_TAB_RIGHT_EDGE:
case NS_THEME_TAB_PANEL:
case NS_THEME_TAB_PANELS:
case NS_THEME_MENUBAR:
case NS_THEME_MENUPOPUP:
case NS_THEME_MENUITEM:
break;
// the dropdown button /almost/ renders correctly with scaling,
// except that the graphic in the dropdown button (the downward arrow)
// doesn't get scaled up.
case NS_THEME_DROPDOWN_BUTTON:
// these are definitely no; they're all graphics that don't get scaled up
case NS_THEME_CHECKBOX:
case NS_THEME_RADIO:
case NS_THEME_CHECKMENUITEM:
case NS_THEME_RADIOMENUITEM:
return
gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
}
return
gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
}
///////////////////////////////////////////
// Creation Routine

View File

@ -116,6 +116,8 @@ protected:
void DrawCheckedRect(HDC hdc, const RECT& rc, PRInt32 fore, PRInt32 back,
HBRUSH defaultBack);
PRUint32 GetWidgetNativeDrawingFlags(PRUint8 aWidgetType);
private:
HMODULE mThemeDLL;
HANDLE mButtonTheme;