mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 05:48:26 +00:00
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:
parent
f0f7c7852c
commit
a50ff1fb29
File diff suppressed because it is too large
Load Diff
@ -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()));
|
||||
}
|
||||
}
|
||||
|
@ -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(¶ms);
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user