mirror of https://github.com/buggins/dlangui.git
subpixel rendering
This commit is contained in:
parent
40c138e911
commit
ae75f40323
|
@ -201,8 +201,10 @@ int pixelsToPoints(int px) {
|
||||||
enum SubpixelRenderingMode : ubyte {
|
enum SubpixelRenderingMode : ubyte {
|
||||||
/// no sub
|
/// no sub
|
||||||
None,
|
None,
|
||||||
/// subpixel rendering on, subpixel order: B,G,R
|
/// subpixel rendering on, subpixel order on device: B,G,R
|
||||||
BGR,
|
BGR,
|
||||||
|
/// subpixel rendering on, subpixel order on device: R,G,B
|
||||||
|
RGB,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -215,6 +217,9 @@ struct Glyph
|
||||||
{
|
{
|
||||||
/// 0: width of glyph black box
|
/// 0: width of glyph black box
|
||||||
ushort blackBoxX;
|
ushort blackBoxX;
|
||||||
|
|
||||||
|
@property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
|
||||||
|
|
||||||
/// 2: height of glyph black box
|
/// 2: height of glyph black box
|
||||||
ubyte blackBoxY;
|
ubyte blackBoxY;
|
||||||
/// 3: X origin for glyph
|
/// 3: X origin for glyph
|
||||||
|
|
|
@ -18,6 +18,8 @@ Authors: Vadim Lopatin, coolreader.org@gmail.com
|
||||||
*/
|
*/
|
||||||
module dlangui.graphics.colors;
|
module dlangui.graphics.colors;
|
||||||
|
|
||||||
|
import dlangui.core.types;
|
||||||
|
|
||||||
private import std.string : strip;
|
private import std.string : strip;
|
||||||
|
|
||||||
/// special color constant to identify value as not a color (to use default/parent value instead)
|
/// special color constant to identify value as not a color (to use default/parent value instead)
|
||||||
|
@ -51,6 +53,37 @@ uint blendARGB(uint dst, uint src, uint alpha) {
|
||||||
return (r << 16) | (g << 8) | b;
|
return (r << 16) | (g << 8) | b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//immutable int COMPONENT_OFFSET_BGR[3] = [2, 1, 0];
|
||||||
|
immutable int COMPONENT_OFFSET_BGR[3] = [2, 1, 0];
|
||||||
|
//immutable int COMPONENT_OFFSET_BGR[3] = [1, 2, 0];
|
||||||
|
immutable int COMPONENT_OFFSET_RGB[3] = [0, 1, 2];
|
||||||
|
immutable int COMPONENT_OFFSET_ALPHA = 3;
|
||||||
|
int subpixelComponentIndex(int x0, SubpixelRenderingMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case SubpixelRenderingMode.RGB:
|
||||||
|
return COMPONENT_OFFSET_BGR[x0];
|
||||||
|
case SubpixelRenderingMode.BGR:
|
||||||
|
default:
|
||||||
|
return COMPONENT_OFFSET_BGR[x0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// blend subpixel using alpha
|
||||||
|
void blendSubpixel(ubyte * dst, ubyte * src, uint alpha, int x0, SubpixelRenderingMode mode) {
|
||||||
|
uint dstalpha = dst[COMPONENT_OFFSET_ALPHA];
|
||||||
|
int offset = subpixelComponentIndex(x0, mode);
|
||||||
|
uint srcr = src[offset];
|
||||||
|
dst[COMPONENT_OFFSET_ALPHA] = 0;
|
||||||
|
if (dstalpha > 0x80) {
|
||||||
|
dst[offset] = cast(ubyte)srcr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint dstr = dst[offset];
|
||||||
|
uint ialpha = 256 - alpha;
|
||||||
|
uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
|
||||||
|
dst[offset] = cast(ubyte)r;
|
||||||
|
}
|
||||||
|
|
||||||
/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
|
/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
|
||||||
uint blendAlpha(uint a1, uint a2) {
|
uint blendAlpha(uint a1, uint a2) {
|
||||||
if (!a1)
|
if (!a1)
|
||||||
|
|
|
@ -562,6 +562,7 @@ class ColorDrawBufBase : DrawBuf {
|
||||||
int srcdy = glyph.blackBoxY;
|
int srcdy = glyph.blackBoxY;
|
||||||
bool clipping = true; //!_clipRect.empty();
|
bool clipping = true; //!_clipRect.empty();
|
||||||
color = applyAlpha(color);
|
color = applyAlpha(color);
|
||||||
|
bool subpixel = glyph.subpixelMode != SubpixelRenderingMode.None;
|
||||||
for (int yy = 0; yy < srcdy; yy++) {
|
for (int yy = 0; yy < srcdy; yy++) {
|
||||||
int liney = y + yy;
|
int liney = y + yy;
|
||||||
if (clipping && (liney < _clipRect.top || liney >= _clipRect.bottom))
|
if (clipping && (liney < _clipRect.top || liney >= _clipRect.bottom))
|
||||||
|
@ -571,24 +572,33 @@ class ColorDrawBufBase : DrawBuf {
|
||||||
uint * row = scanLine(liney);
|
uint * row = scanLine(liney);
|
||||||
ubyte * srcrow = src.ptr + yy * srcdx;
|
ubyte * srcrow = src.ptr + yy * srcdx;
|
||||||
for (int xx = 0; xx < srcdx; xx++) {
|
for (int xx = 0; xx < srcdx; xx++) {
|
||||||
int colx = xx + x;
|
int colx = x + (subpixel ? xx / 3 : xx);
|
||||||
if (clipping && (colx < _clipRect.left || colx >= _clipRect.right))
|
if (clipping && (colx < _clipRect.left || colx >= _clipRect.right))
|
||||||
continue;
|
continue;
|
||||||
if (colx < 0 || colx >= _dx)
|
if (colx < 0 || colx >= _dx)
|
||||||
continue;
|
continue;
|
||||||
uint alpha1 = srcrow[xx] ^ 255;
|
|
||||||
uint alpha2 = (color >> 24);
|
uint alpha2 = (color >> 24);
|
||||||
|
uint alpha1 = srcrow[xx] ^ 255;
|
||||||
uint alpha = ((((alpha1 ^ 255) * (alpha2 ^ 255)) >> 8) ^ 255) & 255;
|
uint alpha = ((((alpha1 ^ 255) * (alpha2 ^ 255)) >> 8) ^ 255) & 255;
|
||||||
|
if (subpixel) {
|
||||||
|
int x0 = xx % 3;
|
||||||
|
ubyte * dst = cast(ubyte*)(row + colx);
|
||||||
|
ubyte * pcolor = cast(ubyte*)(&color);
|
||||||
|
blendSubpixel(dst, pcolor, alpha, x0, glyph.subpixelMode);
|
||||||
|
} else {
|
||||||
uint pixel = row[colx];
|
uint pixel = row[colx];
|
||||||
|
if (alpha < 255) {
|
||||||
if (!alpha)
|
if (!alpha)
|
||||||
row[colx] = pixel;
|
row[colx] = pixel;
|
||||||
else if (alpha < 255) {
|
else {
|
||||||
// apply blending
|
// apply blending
|
||||||
row[colx] = blendARGB(pixel, color, alpha);
|
row[colx] = blendARGB(pixel, color, alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override void fillRect(Rect rc, uint color) {
|
override void fillRect(Rect rc, uint color) {
|
||||||
if (applyClipping(rc)) {
|
if (applyClipping(rc)) {
|
||||||
|
|
|
@ -243,7 +243,7 @@ class Font : RefCountedObject {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int w = x + glyph.width; // using advance
|
int w = x + glyph.width; // using advance
|
||||||
int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
|
int w2 = x + glyph.originX + glyph.correctedBlackBoxX; // using black box
|
||||||
if (w < w2) // choose bigger value
|
if (w < w2) // choose bigger value
|
||||||
w = w2;
|
w = w2;
|
||||||
pwidths[i] = w;
|
pwidths[i] = w;
|
||||||
|
@ -334,7 +334,7 @@ class Font : RefCountedObject {
|
||||||
continue;
|
continue;
|
||||||
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
||||||
int gx = x + xx + glyph.originX;
|
int gx = x + xx + glyph.originX;
|
||||||
if (gx + glyph.blackBoxX < clip.left)
|
if (gx + glyph.correctedBlackBoxX < clip.left)
|
||||||
continue;
|
continue;
|
||||||
buf.drawGlyph( gx,
|
buf.drawGlyph( gx,
|
||||||
y + _baseline - glyph.originY,
|
y + _baseline - glyph.originY,
|
||||||
|
@ -405,7 +405,7 @@ class Font : RefCountedObject {
|
||||||
continue;
|
continue;
|
||||||
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
||||||
int gx = x + xx + glyph.originX;
|
int gx = x + xx + glyph.originX;
|
||||||
if (gx + glyph.blackBoxX < clip.left)
|
if (gx + glyph.correctedBlackBoxX < clip.left)
|
||||||
continue;
|
continue;
|
||||||
buf.drawGlyph( gx,
|
buf.drawGlyph( gx,
|
||||||
y + _baseline - glyph.originY,
|
y + _baseline - glyph.originY,
|
||||||
|
|
|
@ -266,6 +266,7 @@ private class FreeTypeFontFile {
|
||||||
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
|
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
|
||||||
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
|
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
|
||||||
glyph.width = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance)) >> 6);
|
glyph.width = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance)) >> 6);
|
||||||
|
glyph.subpixelMode = SubpixelRenderingMode.None;
|
||||||
//glyph.glyphIndex = cast(ushort)code;
|
//glyph.glyphIndex = cast(ushort)code;
|
||||||
if (withImage) {
|
if (withImage) {
|
||||||
FT_Bitmap* bitmap = &_slot.bitmap;
|
FT_Bitmap* bitmap = &_slot.bitmap;
|
||||||
|
|
|
@ -92,8 +92,8 @@ class GLDrawBuf : DrawBuf {
|
||||||
/// draw 8bit alpha image - usually font glyph using specified color (clipping is applied)
|
/// draw 8bit alpha image - usually font glyph using specified color (clipping is applied)
|
||||||
override void drawGlyph(int x, int y, Glyph * glyph, uint color) {
|
override void drawGlyph(int x, int y, Glyph * glyph, uint color) {
|
||||||
assert(_scene !is null);
|
assert(_scene !is null);
|
||||||
Rect dstrect = Rect(x,y, x + glyph.blackBoxX, y + glyph.blackBoxY);
|
Rect dstrect = Rect(x,y, x + glyph.correctedBlackBoxX, y + glyph.blackBoxY);
|
||||||
Rect srcrect = Rect(0, 0, glyph.blackBoxX, glyph.blackBoxY);
|
Rect srcrect = Rect(0, 0, glyph.correctedBlackBoxX, glyph.blackBoxY);
|
||||||
//Log.v("GLDrawBuf.drawGlyph dst=", dstrect, " src=", srcrect, " color=", color);
|
//Log.v("GLDrawBuf.drawGlyph dst=", dstrect, " src=", srcrect, " color=", color);
|
||||||
color = applyAlpha(color);
|
color = applyAlpha(color);
|
||||||
if (!isFullyTransparentColor(color) && applyClipping(dstrect, srcrect)) {
|
if (!isFullyTransparentColor(color) && applyClipping(dstrect, srcrect)) {
|
||||||
|
@ -659,7 +659,7 @@ private class GLGlyphCache {
|
||||||
return _itemCount;
|
return _itemCount;
|
||||||
}
|
}
|
||||||
GLGlyphCacheItem addItem(Glyph * glyph) {
|
GLGlyphCacheItem addItem(Glyph * glyph) {
|
||||||
GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.blackBoxX, glyph.blackBoxY);
|
GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.correctedBlackBoxX, glyph.blackBoxY);
|
||||||
if (cacheItem is null)
|
if (cacheItem is null)
|
||||||
return null;
|
return null;
|
||||||
_drawbuf.drawGlyph(cacheItem._rc.left, cacheItem._rc.top, glyph, 0xFFFFFF);
|
_drawbuf.drawGlyph(cacheItem._rc.left, cacheItem._rc.top, glyph, 0xFFFFFF);
|
||||||
|
|
|
@ -107,6 +107,22 @@ __gshared static this() {
|
||||||
lut = lcd_distribution_lut!65(0.5, 0.25, 0.125);
|
lut = lcd_distribution_lut!65(0.5, 0.25, 0.125);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int myabs(int n) {
|
||||||
|
return n < 0 ? -n : n;
|
||||||
|
}
|
||||||
|
private int colorStat(ubyte * p) {
|
||||||
|
int avg = (cast(int)p[0] + cast(int)p[1] + cast(int)p[2]) / 3;
|
||||||
|
return myabs(avg - cast(int)p[0]) + myabs(avg - cast(int)p[1]) + myabs(avg - cast(int)p[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int minIndex(int n0, int n1, int n2) {
|
||||||
|
if (n0 <= n1 && n0 <= n2)
|
||||||
|
return 0;
|
||||||
|
if (n1 <= n0 && n1 <= n2)
|
||||||
|
return 1;
|
||||||
|
return n2;
|
||||||
|
}
|
||||||
|
|
||||||
// This function prepares the alpha-channel information
|
// This function prepares the alpha-channel information
|
||||||
// for the glyph averaging the values in accordance with
|
// for the glyph averaging the values in accordance with
|
||||||
// the method suggested by Steve Gibson. The function
|
// the method suggested by Steve Gibson. The function
|
||||||
|
@ -118,8 +134,10 @@ __gshared static this() {
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
ushort prepare_lcd_glyph(ubyte * gbuf1,
|
ushort prepare_lcd_glyph(ubyte * gbuf1,
|
||||||
ref GLYPHMETRICS gm,
|
ref GLYPHMETRICS gm,
|
||||||
ref ubyte[] gbuf2)
|
ref ubyte[] gbuf2,
|
||||||
|
ref int shiftedBy)
|
||||||
{
|
{
|
||||||
|
shiftedBy = 0;
|
||||||
uint src_stride = (gm.gmBlackBoxX + 3) / 4 * 4;
|
uint src_stride = (gm.gmBlackBoxX + 3) / 4 * 4;
|
||||||
uint dst_width = src_stride + 4;
|
uint dst_width = src_stride + 4;
|
||||||
gbuf2 = new ubyte[dst_width * gm.gmBlackBoxY];
|
gbuf2 = new ubyte[dst_width * gm.gmBlackBoxY];
|
||||||
|
@ -128,8 +146,7 @@ ushort prepare_lcd_glyph(ubyte * gbuf1,
|
||||||
{
|
{
|
||||||
ubyte * src_ptr = gbuf1 + src_stride * y;
|
ubyte * src_ptr = gbuf1 + src_stride * y;
|
||||||
ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
|
ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
|
||||||
uint x;
|
for(uint x = 0; x < gm.gmBlackBoxX; ++x)
|
||||||
for(x = 0; x < gm.gmBlackBoxX; ++x)
|
|
||||||
{
|
{
|
||||||
uint v = *src_ptr++;
|
uint v = *src_ptr++;
|
||||||
dst_ptr[0] += lut.tertiary(v);
|
dst_ptr[0] += lut.tertiary(v);
|
||||||
|
@ -140,6 +157,31 @@ ushort prepare_lcd_glyph(ubyte * gbuf1,
|
||||||
++dst_ptr;
|
++dst_ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
int dx = (dst_width - 2) / 3;
|
||||||
|
int stats[3] = [0, 0, 0];
|
||||||
|
for (uint y = 0; y < gm.gmBlackBoxY; y++) {
|
||||||
|
for(uint x = 0; x < dx; ++x)
|
||||||
|
{
|
||||||
|
for (uint x0 = 0; x0 < 3; x0++) {
|
||||||
|
stats[x0] += colorStat(gbuf2.ptr + dst_width * y + x0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shiftedBy = 0; //minIndex(stats[0], stats[1], stats[2]);
|
||||||
|
if (shiftedBy) {
|
||||||
|
for (uint y = 0; y < gm.gmBlackBoxY; y++) {
|
||||||
|
ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
|
||||||
|
for(uint x = 0; x < gm.gmBlackBoxX; ++x)
|
||||||
|
{
|
||||||
|
if (x + shiftedBy < gm.gmBlackBoxX)
|
||||||
|
dst_ptr[x] = dst_ptr[x + shiftedBy];
|
||||||
|
else
|
||||||
|
dst_ptr[x] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
return cast(ushort) dst_width;
|
return cast(ushort) dst_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,9 +313,9 @@ class Win32Font : Font {
|
||||||
}
|
}
|
||||||
g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
|
g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
|
||||||
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
|
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
|
||||||
g.originX = cast(byte)(needSubpixelRendering ? metrics.gmptGlyphOrigin.x / 3: metrics.gmptGlyphOrigin.x);
|
g.originX = cast(byte)(needSubpixelRendering ? ((metrics.gmptGlyphOrigin.x + 0) / 3) : (metrics.gmptGlyphOrigin.x));
|
||||||
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
|
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
|
||||||
g.width = cast(ubyte)(needSubpixelRendering ? metrics.gmCellIncX / 3 : metrics.gmCellIncX);
|
g.width = cast(ubyte)(needSubpixelRendering ? (metrics.gmCellIncX + 0) / 3 : metrics.gmCellIncX);
|
||||||
g.subpixelMode = needSubpixelRendering ? FontManager.subpixelRenderingMode : SubpixelRenderingMode.None;
|
g.subpixelMode = needSubpixelRendering ? FontManager.subpixelRenderingMode : SubpixelRenderingMode.None;
|
||||||
//g.glyphIndex = cast(ushort)glyphIndex;
|
//g.glyphIndex = cast(ushort)glyphIndex;
|
||||||
|
|
||||||
|
@ -296,11 +338,14 @@ class Win32Font : Font {
|
||||||
}
|
}
|
||||||
if (needSubpixelRendering) {
|
if (needSubpixelRendering) {
|
||||||
ubyte[] newglyph;
|
ubyte[] newglyph;
|
||||||
|
int shiftedBy = 0;
|
||||||
g.blackBoxX = prepare_lcd_glyph(glyph.ptr,
|
g.blackBoxX = prepare_lcd_glyph(glyph.ptr,
|
||||||
metrics,
|
metrics,
|
||||||
newglyph);
|
newglyph,
|
||||||
|
shiftedBy);
|
||||||
g.glyph = newglyph;
|
g.glyph = newglyph;
|
||||||
//g.width = g.width / 3;
|
//g.originX = cast(ubyte)((metrics.gmptGlyphOrigin.x + 2 - shiftedBy) / 3);
|
||||||
|
//g.width = cast(ubyte)((metrics.gmCellIncX + 2 - shiftedBy) / 3);
|
||||||
} else {
|
} else {
|
||||||
int glyph_row_size = (g.blackBoxX + 3) / 4 * 4;
|
int glyph_row_size = (g.blackBoxX + 3) / 4 * 4;
|
||||||
ubyte * src = glyph.ptr;
|
ubyte * src = glyph.ptr;
|
||||||
|
|
|
@ -315,7 +315,7 @@
|
||||||
layoutHeight="WRAP_CONTENT"
|
layoutHeight="WRAP_CONTENT"
|
||||||
fontFace="Arial"
|
fontFace="Arial"
|
||||||
fontFamily="SansSerif"
|
fontFamily="SansSerif"
|
||||||
fontSize="12">
|
fontSize="16">
|
||||||
</style>
|
</style>
|
||||||
<style id="TREE_ITEM_EXPAND_ICON"
|
<style id="TREE_ITEM_EXPAND_ICON"
|
||||||
margins="2,0,2,0"
|
margins="2,0,2,0"
|
||||||
|
@ -338,7 +338,7 @@
|
||||||
layoutHeight="WRAP_CONTENT"
|
layoutHeight="WRAP_CONTENT"
|
||||||
align="Left|VCenter"
|
align="Left|VCenter"
|
||||||
textFlags="Parent"
|
textFlags="Parent"
|
||||||
fontSize="12"
|
fontSize="16"
|
||||||
/>
|
/>
|
||||||
<style id="RESIZER_VERTICAL"
|
<style id="RESIZER_VERTICAL"
|
||||||
layoutWidth="FILL_PARENT"
|
layoutWidth="FILL_PARENT"
|
||||||
|
|
Loading…
Reference in New Issue