Bug 622482 - Part 4: Manually draw glyphs when needing subpixel AA on an RGBA surface. r=jrmuizel a=blocking-betaN

This commit is contained in:
Bas Schouten 2011-01-16 03:30:01 +01:00
parent f0f7c7852c
commit a50ff1fb29
3 changed files with 1220 additions and 252 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ cbuffer cb0
{
float4 QuadDesc;
float4 TexCoords;
float4 TextColor;
}
struct VS_OUTPUT
@ -21,8 +22,27 @@ struct VS_OUTPUT
float2 TexCoord : TEXCOORD0;
};
struct PS_OUTPUT
{
float4 color;
float4 alpha;
};
Texture2D tex;
BlendState bTextBlend
{
AlphaToCoverageEnable = FALSE;
BlendEnable[0] = TRUE;
SrcBlend = Src1_Color;
DestBlend = Inv_Src1_Color;
BlendOp = Add;
SrcBlendAlpha = Src1_Alpha;
DestBlendAlpha = Inv_Src1_Alpha;
BlendOpAlpha = Add;
RenderTargetWriteMask[0] = 0x0F; // All
};
sampler sSampler = sampler_state {
Texture = tex;
AddressU = Clamp;
@ -46,6 +66,14 @@ float4 SampleTexturePS( VS_OUTPUT In) : SV_Target
return tex.Sample(sSampler, In.TexCoord);
};
PS_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target
{
PS_OUTPUT output;
output.color = TextColor;
output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a;
return output;
};
technique10 SampleTexture
{
pass P0
@ -55,3 +83,14 @@ technique10 SampleTexture
SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
}
}
technique10 SampleTextTexture
{
pass P0
{
SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS()));
}
}

View File

@ -715,6 +715,19 @@ _d2d_clear_surface(cairo_d2d_surface_t *surf)
surf->rt->EndDraw();
}
static cairo_rectangle_int_t
_cairo_rect_from_windows_rect(const RECT *rect)
{
cairo_rectangle_int_t new_rect;
new_rect.x = rect->left;
new_rect.y = rect->top;
new_rect.width = rect->right - rect->left;
new_rect.height = rect->bottom - rect->top;
return new_rect;
}
static D2D1_POINT_2F
_d2d_point_from_cairo_point(const cairo_point_t *point)
{
@ -3474,6 +3487,278 @@ _cairo_d2d_fill(void *surface,
return CAIRO_INT_STATUS_SUCCESS;
}
cairo_int_status_t
_cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface,
cairo_operator_t op,
const cairo_solid_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_dwrite_scaled_font_t *scaled_font,
cairo_clip_t *clip)
{
cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->base.font_face);
cairo_d2d_surface_t *dst = reinterpret_cast<cairo_d2d_surface_t*>(surface);
BOOL transform = FALSE;
HRESULT hr;
cairo_region_t *clip_region = NULL;
// We can only draw axis and pixel aligned rectangular quads, this means we
// can only support clips which form regions, since the intersection with
// our text area will then always be a set of rectangular axis and pixel
// aligned quads.
if (clip) {
_cairo_clip_get_region(clip, &clip_region);
if (!clip_region) {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
_cairo_d2d_set_clip(dst, NULL);
dst->rt->Flush();
DWRITE_GLYPH_RUN run;
_cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, scaled_font, &run, &transform);
RefPtr<IDWriteGlyphRunAnalysis> analysis;
DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat);
RefPtr<IDWriteRenderingParams> params;
dst->rt->GetTextRenderingParams(&params);
DWRITE_RENDERING_MODE renderMode;
dwriteff->dwriteface->GetRecommendedRenderingMode((FLOAT)scaled_font->base.font_matrix.yy,
1.0f,
DWRITE_MEASURING_MODE_NATURAL,
params,
&renderMode);
// Deal with rendering modes CreateGlyphRunAnalysis doesn't accept.
if (renderMode == DWRITE_RENDERING_MODE_DEFAULT) {
// As per DWRITE_RENDERING_MODE documentation, pick Natural for font
// sizes under 16 ppem
if (scaled_font->base.font_matrix.yy < 16.0f) {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
} else {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
}
} else if (renderMode == DWRITE_RENDERING_MODE_OUTLINE) {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
}
DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
1.0f,
transform ? &dwmat : 0,
renderMode,
DWRITE_MEASURING_MODE_NATURAL,
0,
0,
&analysis);
delete [] run.glyphIndices;
delete [] run.glyphAdvances;
delete [] run.glyphOffsets;
RECT bounds;
analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1,
&bounds);
cairo_rectangle_int_t cairo_bounds =
_cairo_rect_from_windows_rect(&bounds);
cairo_region_t *region;
if (clip) {
region = cairo_region_copy(clip_region);
cairo_region_intersect_rectangle(region, &cairo_bounds);
} else {
region = cairo_region_create_rectangle(&cairo_bounds);
}
cairo_region_auto_ptr region_ptr(region);
if (cairo_region_is_empty(region)) {
// Nothing to do.
return CAIRO_INT_STATUS_SUCCESS;
}
int bufferSize = cairo_bounds.width * cairo_bounds.height * 3;
if (!bufferSize) {
// width == 0 || height == 0
return CAIRO_INT_STATUS_SUCCESS;
}
// We add one byte so we can safely read an entire 32-bit int when copying
// the last 3 bytes of the alpha texture.
BYTE *texture = new BYTE[bufferSize + 1];
analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize);
RefPtr<ID3D10ShaderResourceView> srView;
ID3D10Device1 *device = dst->device->mD3D10Device;
int textureWidth, textureHeight;
if (cairo_bounds.width < TEXT_TEXTURE_WIDTH &&
cairo_bounds.height < TEXT_TEXTURE_HEIGHT)
{
// Use our cached TextTexture when it is big enough.
D3D10_MAPPED_TEXTURE2D texMap;
hr = dst->device->mTextTexture->Map(0, D3D10_MAP_WRITE_DISCARD, 0, &texMap);
if (FAILED(hr)) {
delete [] texture;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
BYTE *alignedTextureData = (BYTE*)texMap.pData;
for (int y = 0; y < cairo_bounds.height; y++) {
for (int x = 0; x < cairo_bounds.width; x++) {
// Copy 3 Bpp source to 4 Bpp texture.
//
// Since we don't care what ends up in the alpha pixel of the
// destination, therefor we can simply copy a normal 32 bit
// integer each time, filling the alpha pixel of the destination
// with the first subpixel of the next pixel from the source.
*((int*)(alignedTextureData + (y * texMap.RowPitch) + x * 4)) =
*((int*)(texture + (y * cairo_bounds.width + x) * 3));
}
}
dst->device->mTextTexture->Unmap(0);
delete [] texture;
srView = dst->device->mTextTextureView;
textureWidth = TEXT_TEXTURE_WIDTH;
textureHeight = TEXT_TEXTURE_HEIGHT;
} else {
int alignedBufferSize = cairo_bounds.width * cairo_bounds.height * 4;
// Create a one-off immutable texture from system memory.
BYTE *alignedTextureData = new BYTE[alignedBufferSize];
for (int y = 0; y < cairo_bounds.height; y++) {
for (int x = 0; x < cairo_bounds.width; x++) {
// Copy 3 Bpp source to 4 Bpp destination memory used for
// texture creation. D3D10 has no 3 Bpp texture format we can
// use.
//
// Since we don't care what ends up in the alpha pixel of the
// destination, therefor we can simply copy a normal 32 bit
// integer each time, filling the alpha pixel of the destination
// with the first subpixel of the next pixel from the source.
*((int*)(alignedTextureData + (y * cairo_bounds.width + x) * 4)) =
*((int*)(texture + (y * cairo_bounds.width + x) * 3));
}
}
D3D10_SUBRESOURCE_DATA data;
CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
cairo_bounds.width, cairo_bounds.height,
1, 1);
desc.Usage = D3D10_USAGE_IMMUTABLE;
data.SysMemPitch = cairo_bounds.width * 4;
data.pSysMem = alignedTextureData;
RefPtr<ID3D10Texture2D> tex;
hr = device->CreateTexture2D(&desc, &data, &tex);
delete [] alignedTextureData;
delete [] texture;
if (FAILED(hr)) {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
hr = device->CreateShaderResourceView(tex, NULL, &srView);
if (FAILED(hr)) {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
textureWidth = cairo_bounds.width;
textureHeight = cairo_bounds.height;
}
// Prepare destination surface for rendering.
RefPtr<ID3D10Texture2D> dstTexture;
dst->surface->QueryInterface(&dstTexture);
if (!dst->buffer_rt_view) {
hr = device->CreateRenderTargetView(dstTexture, NULL, &dst->buffer_rt_view);
if (FAILED(hr)) {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
D3D10_TEXTURE2D_DESC tDesc;
dstTexture->GetDesc(&tDesc);
D3D10_VIEWPORT vp;
vp.Height = tDesc.Height;
vp.MinDepth = 0;
vp.MaxDepth = 1.0;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = tDesc.Width;
device->RSSetViewports(1, &vp);
ID3D10Effect *effect = dst->device->mSampleEffect;
ID3D10RenderTargetView *rtViewPtr = dst->buffer_rt_view;
device->OMSetRenderTargets(1, &rtViewPtr, 0);
ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTextTexture");
ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector();
ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector();
ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector();
float colorVal[] = { float(source->color.red * source->color.alpha),
float(source->color.green * source->color.alpha),
float(source->color.blue * source->color.alpha),
float(source->color.alpha) };
textColor->SetFloatVector(colorVal);
float quadDescVal[4];
float texCoordsVal[4];
// Draw a quad for each rectangle in the intersection of the clip and the
// text area.
for (int i = 0; i < cairo_region_num_rectangles(region); i++) {
cairo_rectangle_int_t quad;
cairo_region_get_rectangle(region, i, &quad);
quadDescVal[0] = -1.0f + ((float)quad.x / (float)tDesc.Width) * 2.0f;
quadDescVal[1] = 1.0f - ((float)quad.y / (float)tDesc.Height) * 2.0f;
quadDescVal[2] = ((float)quad.width / (float)tDesc.Width) * 2.0f;
quadDescVal[3] = -((float)quad.height / (float)tDesc.Height) * 2.0f;
texCoordsVal[0] = (float)(quad.x - cairo_bounds.x) / textureWidth;
texCoordsVal[1] = (float)(quad.y - cairo_bounds.y) / textureHeight;
texCoordsVal[2] = (float)quad.width / textureWidth;
texCoordsVal[3] = (float)quad.height / textureHeight;
quadDesc->SetFloatVector(quadDescVal);
texCoords->SetFloatVector(texCoordsVal);
_cairo_d2d_setup_for_blend(dst->device);
technique->GetPassByIndex(0)->Apply(0);
ID3D10ShaderResourceView *srViewPtr = srView;
device->PSSetShaderResources(0, 1, &srViewPtr);
device->Draw(4, 0);
}
return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_dwrite_show_glyphs_on_d2d_surface(void *surface,
cairo_operator_t op,
@ -3502,6 +3787,32 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface,
return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (op == CAIRO_OPERATOR_OVER && source->type == CAIRO_PATTERN_TYPE_SOLID &&
dst->base.content != CAIRO_CONTENT_COLOR &&
dst->base.permit_subpixel_antialiasing &&
dwritesf->antialias_mode == CAIRO_ANTIALIAS_SUBPIXEL)
{
// The D2D/DWrite drawing API's will not allow drawing subpixel AA to
// an RGBA surface. We do however want to do this if we know all text
// on a surface will be over opaque pixels, when this is the case
// we set the permit_subpixel_antialiasing flag on a surface. We then
// proceed to manually composite the glyphs to the surface.
const cairo_solid_pattern_t *solid_src =
reinterpret_cast<const cairo_solid_pattern_t*>(source);
status = _cairo_dwrite_manual_show_glyphs_on_d2d_surface(surface,
op,
solid_src,
glyphs,
num_glyphs,
dwritesf,
clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
return status;
}
}
RefPtr<ID2D1RenderTarget> target_rt = dst->rt;
cairo_rectangle_int_t fontArea;
#ifndef ALWAYS_MANUAL_COMPOSITE