skia2/samplecode/SampleText.cpp
reed@android.com 6b82d1adc6 add isConvex() hit to SkPath, to be used to speed up fills and opengl
set linewidth in gldevice for hair rects
remove some cruft from samples
add more gl-unimpl messages



git-svn-id: http://skia.googlecode.com/svn/trunk@199 2bbb7eff-a529-9590-31e7-b0007b416f81
2009-06-03 02:35:01 +00:00

794 lines
22 KiB
C++

#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "Sk64.h"
#include "SkGradientShader.h"
#include "SkGraphics.h"
#include "SkImageDecoder.h"
#include "SkKernel33MaskFilter.h"
#include "SkPath.h"
#include "SkRandom.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkColorPriv.h"
#include "SkColorFilter.h"
#include "SkTime.h"
#include "SkTypeface.h"
#include "SkXfermode.h"
#include "SkStream.h"
#include "SkXMLParser.h"
static const int gKernel[3][3] = {
// { -1, -2, -1 }, { -2, 12, -2 }, { -1, -2, -1 }
{ 1, 2, 1 }, { 2, 64-12, 2 }, { 1, 2, 1 }
};
static const int gShift = 6;
class ReduceNoise : public SkKernel33ProcMaskFilter {
public:
ReduceNoise(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
virtual uint8_t computeValue(uint8_t* const* srcRows)
{
int c = srcRows[1][1];
int min = 255, max = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (i != 1 || j != 1)
{
int v = srcRows[i][j];
if (max < v)
max = v;
if (min > v)
min = v;
}
if (c > max) c = max;
// if (c < min) c = min;
return c;
}
virtual Factory getFactory() { return Create; }
private:
ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
static SkFlattenable* Create(SkFlattenableReadBuffer& rb)
{
return new ReduceNoise(rb);
}
};
class Darken : public SkKernel33ProcMaskFilter {
public:
Darken(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
virtual uint8_t computeValue(uint8_t* const* srcRows)
{
int c = srcRows[1][1];
float f = c / 255.f;
if (c >= 0)
{
f = sqrtf(f);
}
else
{
f *= f;
}
SkASSERT(f >= 0 && f <= 1);
return (int)(f * 255);
}
virtual Factory getFactory() { return Create; }
private:
Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
static SkFlattenable* Create(SkFlattenableReadBuffer& rb)
{
return new Darken(rb);
}
};
static SkMaskFilter* makemf() { return new Darken(0x30); }
//#ifdef TEST_CLICKX
static void test_typefaceCache()
{
#ifdef ANDROID
SkTypeface* t0 = SkTypeface::CreateFromName("sans-serif",
SkTypeface::kNormal);
SkTypeface* t1 = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
SkTypeface* t2 = SkTypeface::CreateFromName("arial", SkTypeface::kNormal);
SkTypeface* t3 = SkTypeface::CreateFromName("helvetica", SkTypeface::kItalic);
SkASSERT(t0 == t1);
SkASSERT(t0 == t2);
SkASSERT(t0 == t3);
#endif
}
static void test_breakText()
{
SkPaint paint;
const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj";
size_t length = strlen(text);
SkScalar width = paint.measureText(text, length);
SkScalar mm = 0;
SkScalar nn = 0;
for (SkScalar w = 0; w <= width; w += SK_Scalar1)
{
SkScalar m;
size_t n = paint.breakText(text, length, w, &m,
SkPaint::kBackward_TextBufferDirection);
SkASSERT(n <= length);
SkASSERT(m <= width);
if (n == 0)
SkASSERT(m == 0);
else
{
// now assert that we're monotonic
if (n == nn)
SkASSERT(m == mm);
else
{
SkASSERT(n > nn);
SkASSERT(m > mm);
}
}
nn = n;
mm = m;
}
nn = paint.breakText(text, length, width, &mm);
SkASSERT(nn == length);
SkASSERT(mm == width);
}
static SkRandom gRand;
class SkPowerMode : public SkXfermode {
public:
SkPowerMode(SkScalar exponent) { this->init(exponent); }
virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[]);
typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
// overrides for SkFlattenable
virtual Factory getFactory() { return Create; }
virtual void flatten(SkFlattenableWriteBuffer& b)
{
// this->INHERITED::flatten(b); How can we know if this is legal????
b.write32(SkScalarToFixed(fExp));
}
private:
SkScalar fExp; // user's value
uint8_t fTable[256]; // cache
void init(SkScalar exponent);
SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b)
{
// read the exponent
this->init(SkFixedToScalar(b.readS32()));
}
static SkFlattenable* Create(SkFlattenableReadBuffer& b)
{
return SkNEW_ARGS(SkPowerMode, (b));
}
typedef SkXfermode INHERITED;
};
void SkPowerMode::init(SkScalar e)
{
fExp = e;
float ee = SkScalarToFloat(e);
printf("------ %g\n", ee);
for (int i = 0; i < 256; i++)
{
float x = i / 255.f;
// printf(" %d %g", i, x);
x = powf(x, ee);
// printf(" %g", x);
int xx = SkScalarRound(SkFloatToScalar(x * 255));
// printf(" %d\n", xx);
fTable[i] = SkToU8(xx);
}
}
void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count, const SkAlpha aa[])
{
for (int i = 0; i < count; i++)
{
SkPMColor c = src[i];
int r = SkGetPackedR32(c);
int g = SkGetPackedG32(c);
int b = SkGetPackedB32(c);
r = fTable[r];
g = fTable[g];
b = fTable[b];
dst[i] = SkPack888ToRGB16(r, g, b);
}
}
static const struct {
const char* fName;
uint32_t fFlags;
bool fFlushCache;
} gHints[] = {
{ "Linear", SkPaint::kLinearText_Flag, false },
{ "Normal", 0, true },
{ "Subpixel", SkPaint::kSubpixelText_Flag, true }
};
#ifdef SK_DEBUG
#define REPEAT_COUNT 1
#else
#define REPEAT_COUNT 5000
#endif
static int count_char_points(const SkPaint& paint, char c)
{
SkPath path;
paint.getTextPath(&c, 1, 0, 0, &path);
return path.getPoints(NULL, 0);
}
static int gOld, gNew, gCount;
static void dump(int c, int oldc, int newc)
{
if (oldc != newc)
{
gOld += oldc;
gNew += newc;
gCount += 1;
printf("char %c: old = %3d, new = %3d, reduction %g%%\n", c, oldc, newc, 100. * (oldc - newc) / oldc);
}
}
static void tab(int n)
{
// printf("[%d] ", n); return;
SkASSERT(n >= 0);
for (int i = 0; i < n; i++)
printf(" ");
}
#if 0
#include "badrects.cpp"
static void make_badrgn(SkRegion* rgn, int insetAmount)
{
SkRect16 r, bounds;
int i;
rgn->setEmpty();
bounds.setEmpty();
for (i = 0; i < SK_ARRAY_COUNT(badrects); i++)
{
SkASSERT(badrects[i].width > 0 && badrects[i].height > 0);
r.set(badrects[i].x, badrects[i].y, badrects[i].x + badrects[i].width, badrects[i].y + badrects[i].height);
r.inset(insetAmount, insetAmount);
rgn->op(r, SkRegion::kUnion_Op);
bounds.join(r);
}
SkASSERT(bounds == rgn->getBounds());
for (i = 0; i < SK_ARRAY_COUNT(badrects); i++)
{
r.set(badrects[i].x, badrects[i].y, badrects[i].x + badrects[i].width, badrects[i].y + badrects[i].height);
SkASSERT(rgn->contains(r));
}
}
#endif
static void draw_rgn(const SkRegion& rgn, SkCanvas* canvas, const SkPaint& paint)
{
SkRect r;
SkRegion::Iterator iter(rgn);
for (; !iter.done(); iter.next())
{
r.set(iter.rect());
canvas->drawRect(r, paint);
}
}
static void test_break(SkCanvas* canvas, const char text[], size_t length,
SkScalar x, SkScalar y, const SkPaint& paint,
SkScalar clickX)
{
SkPaint linePaint;
linePaint.setAntiAlias(true);
SkScalar measured;
if (paint.breakText(text, length, clickX - x, &measured, SkPaint::kForward_TextBufferDirection))
{
linePaint.setColor(SK_ColorRED);
canvas->drawLine(x, y, x + measured, y, linePaint);
}
x += paint.measureText(text, length);
if (paint.breakText(text, length, x - clickX, &measured, SkPaint::kBackward_TextBufferDirection))
{
linePaint.setColor(SK_ColorBLUE);
canvas->drawLine(x - measured, y, x, y, linePaint);
}
}
static void test_poly()
{
static const SkPoint dst[] = {
SkIntToScalar(2), SkIntToScalar(1),
SkIntToScalar(5), SkIntToScalar(1),
SkIntToScalar(5), SkIntToScalar(3),
SkIntToScalar(2), SkIntToScalar(3)
};
static const SkPoint src[] = {
SkIntToScalar(0), SkIntToScalar(0),
SkIntToScalar(1), SkIntToScalar(0),
SkIntToScalar(1), SkIntToScalar(1),
SkIntToScalar(0), SkIntToScalar(1)
};
SkMatrix matrix;
if (matrix.setPolyToPoly(src, dst, 4))
{
SkPoint pt = { SK_Scalar1/2, SK_Scalar1/2 };
matrix.mapPoints(&pt, 1);
printf("---- x = %g y = %g\n", SkScalarToFloat(pt.fX), SkScalarToFloat(pt.fY));
}
else
printf("---- setPolyToPoly failed\n");
}
#include "SkColorShader.h"
static void DrawTheText(SkCanvas* canvas, const char text[], size_t length,
SkScalar x, SkScalar y, const SkPaint& paint,
SkScalar clickX, SkMaskFilter* mf)
{
SkPaint p(paint);
#if 0
canvas->drawText(text, length, x, y, paint);
#else
{
SkPoint pts[1000];
SkScalar xpos = x;
SkASSERT(length <= SK_ARRAY_COUNT(pts));
for (size_t i = 0; i < length; i++)
pts[i].set(xpos, y), xpos += paint.getTextSize();
canvas->drawPosText(text, length, pts, paint);
}
#endif
p.setSubpixelText(true);
x += SkIntToScalar(180);
canvas->drawText(text, length, x, y, p);
#ifdef TEST_CLICKX
test_break(canvas, text, length, x, y, p, clickX);
#endif
#ifdef SK_DEBUG
if (false)
{
SkColorShader shader;
p.setShader(&shader);
x += SkIntToScalar(180);
canvas->drawText(text, length, x, y, p);
p.setShader(NULL);
}
if (true)
{
// p.setMaskFilter(mf);
p.setSubpixelText(false);
p.setLinearText(true);
x += SkIntToScalar(180);
canvas->drawText(text, length, x, y, p);
}
#endif
}
class TextSpeedView : public SkView {
public:
TextSpeedView()
{
fMF = makemf();
fHints = 0;
if (false)
{
static const char extra[] = { '.', ',', ':', ';', '!' };
SkPaint paint, paint2;
paint2.setTypeface(SkTypeface::CreateFromName(NULL,
SkTypeface::kItalic))->unref();
for (int i = 0; i < 26; i++)
::dump('a' + i, count_char_points(paint, 'a' + i), count_char_points(paint2, 'a' + i));
for (int j = 0; j < SK_ARRAY_COUNT(extra); j++)
::dump(extra[j], count_char_points(paint, extra[j]), count_char_points(paint2, extra[j]));
printf("--- ave reduction = %g%%\n", 100. * (gOld - gNew) / gOld);
}
if (true)
{
SkPoint pts[] = { SkIntToScalar(20), 0, SkIntToScalar(256+20), 0 };
SkColor colors[] = { SkColorSetARGB(0, 255, 255, 255), SkColorSetARGB(255, 255, 255, 255) };
fGradient = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
}
fClickX = 0;
test_breakText();
test_typefaceCache();
// test_poly();
}
virtual ~TextSpeedView()
{
fGradient->unref();
fMF->safeUnref();
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt)
{
if (SampleCode::TitleQ(*evt))
{
SampleCode::TitleR(evt, "Text");
return true;
}
return this->INHERITED::onQuery(evt);
}
void drawBG(SkCanvas* canvas)
{
// canvas->drawColor(0xFFDDDDDD);
canvas->drawColor(SK_ColorWHITE);
// canvas->drawColor(SK_ColorBLACK);
}
static void make_textstrip(SkBitmap* bm)
{
bm->setConfig(SkBitmap::kRGB_565_Config, 200, 18);
bm->allocPixels();
bm->eraseColor(SK_ColorWHITE);
SkCanvas canvas(*bm);
SkPaint paint;
const char* s = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit";
paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag
| SkPaint::kDevKernText_Flag);
paint.setTextSize(SkIntToScalar(14));
canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint);
}
static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand)
{
for (size_t i = 0; i < n; i++)
pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
}
virtual void onDraw(SkCanvas* canvas)
{
if (false)
{
canvas->translate(SkIntToScalar(480), 0);
canvas->rotate(SkIntToScalar(90));
}
this->drawBG(canvas);
if (false)
{
SkPaint p;
p.setAntiAlias(true);
p.setSubpixelText(true);
// p.setLinearText(true);
SkScalar size = SkIntToScalar(6);
SkMSec dur = 0;
const int LOOP = 16;
const int TIMES = 10;
for (int times = 0; times < TIMES; times++)
{
SkMSec now = SkTime::GetMSecs();
for (int loop = 0; loop < LOOP; loop++)
{
p.setTextSize(size);
size += SK_Scalar1/5;
canvas->drawText("Hamburgefons", 12, SkIntToScalar(10), SkIntToScalar(50), p);
}
dur += SkTime::GetMSecs() - now;
SkGraphics::SetFontCacheUsed(0);
}
printf("----- duration = %g\n", dur * 1.0 / TIMES);
this->inval(NULL);
return;
}
if (false)
{
SkPaint p;
p.setAntiAlias(true);
for (int i = 6; i <= 36; i++)
{
SkRect r;
SkPaint::FontMetrics m;
p.setTextSize(SkIntToScalar(i));
p.getFontMetrics(&m);
int ascent = SkScalarRound(m.fAscent);
int descent = SkScalarRound(m.fDescent);
for (uint8_t c = ' '; c <= 127; c++)
{
p.getTextWidths(&c, 1, NULL, &r);
if (SkScalarRound(r.fTop) < ascent)
printf("PS %d --- %c [%d] top=%g, ascent=%g ymax=%g\n", i, c, c,
SkScalarToFloat(r.fTop), SkScalarToFloat(m.fAscent), SkScalarToFloat(m.fTop));
if (SkScalarRound(r.fBottom) > descent)
printf("PS %d --- %c [%d] bottom=%g, descent=%g ymin=%g\n", i, c, c,
SkScalarToFloat(r.fBottom), SkScalarToFloat(m.fDescent), SkScalarToFloat(m.fBottom));
}
}
}
if (false)
{
SkPaint p;
p.setShader(fGradient);
#ifdef SK_RELEASE
SkMSec now = SkTime::GetMSecs();
for (int i = 0; i < 100; i++)
#endif
canvas->drawPaint(p);
#ifdef SK_RELEASE
printf("----- %d ms\n", SkTime::GetMSecs() - now);
this->inval(NULL);
#endif
return;
}
if (false)
{
SkBitmap bm;
make_textstrip(&bm);
canvas->translate(0, SkIntToScalar(50));
for (int i = 0; i < 10; i++)
{
float gamma = 1 + i * 0.2f;
SkPowerMode mode(SkFloatToScalar(1 / gamma));
SkPaint p;
p.setXfermode(&mode);
canvas->drawBitmap(bm, 0, SkIntToScalar(i) * bm.height(), &p);
}
return;
}
if (false)
{
SkPaint paint;
paint.setAntiAlias(true);
paint.setDevKernText(true);
SkMSec now = SkTime::GetMSecs();
for (int i = 0; i < 1000000; i++)
{
paint.measureText("Hamburgefons", 15, NULL, NULL);
}
printf("--------- measure %d\n", SkTime::GetMSecs() - now);
this->inval(NULL);
return;
}
if (false)
{
SkRegion rgn;
SkPath path;
SkPaint paint;
// make_badrgn(&rgn, -2);
if (false)
{
paint.setColor(SK_ColorBLUE);
canvas->drawIRect(rgn.getBounds(), paint);
}
paint.setColor(SK_ColorRED);
draw_rgn(rgn, canvas, paint);
rgn.getBoundaryPath(&path);
paint.setARGB(0x80, 0, 0, 0xFF);
canvas->drawPath(path, paint);
return;
}
if (false)
{
SkRect r = { SkIntToScalar(50), SkIntToScalar(50), SkIntToScalar(300), SkIntToScalar(300) };
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setAlpha(0x80);
p.setStrokeWidth(SkIntToScalar(20));
canvas->drawRect(r, p);
}
if (false)
{
SkPaint p;
SkRect r = { SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(104), SkIntToScalar(104) };
// r.offset(SK_ScalarHalf, SK_ScalarHalf);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1*2);
// p.setAntiAliasOn(true);
canvas->drawRect(r, p);
return;
}
if (false)
{
Sk64 aa, bb;
int64_t a = (int64_t)6062080 * -30596;
int64_t b = (int64_t)4816896 * 57957;
aa.setMul(6062080, -30596);
bb.setMul(4816896, 57957);
a += b;
b = a >> 16;
// SkFixed c = aa.addGetFixed(bb);
printf("%d %d\n", (int)a, a >> 32);
SkBitmap bm;
SkPaint paint;
SkScalar scale = SkFloatToScalar(0.5625f);
SkScalar x = SkIntToScalar(100);
SkScalar y = SkIntToScalar(100);
//paint.setFilterType(SkPaint::kBilinear_FilterType);
SkImageDecoder::DecodeFile("/app_web_browser.png", &bm);
// canvas->drawBitmap(bm, x, y, paint);
x += SkIntToScalar(100);
canvas->save();
canvas->translate(x, y);
canvas->scale(SkIntToScalar(2)/1, SkIntToScalar(2)/1);
canvas->translate(-x, -y);
canvas->drawBitmap(bm, x, y, &paint);
canvas->restore();
x += SkIntToScalar(100);
canvas->save();
canvas->translate(x, y);
canvas->scale(scale, scale);
canvas->translate(-x, -y);
// canvas->drawBitmap(bm, x, y, paint);
canvas->restore();
return;
}
SkAutoCanvasRestore restore(canvas, false);
{
SkRect r;
r.set(0, 0, SkIntToScalar(1000), SkIntToScalar(20));
// canvas->saveLayer(&r, NULL, SkCanvas::kHasAlphaLayer_SaveFlag);
}
SkPaint paint;
// const uint16_t glyphs[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
int index = fHints % SK_ARRAY_COUNT(gHints);
index = 1;
// const char* style = gHints[index].fName;
// canvas->translate(0, SkIntToScalar(50));
// canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint);
// paint.setTypeface(SkTypeface::Create(NULL, SkTypeface::kItalic))->unref();
paint.setAntiAlias(true);
paint.setFlags(paint.getFlags() | gHints[index].fFlags);
SkMSec now = 0;
if (REPEAT_COUNT > 1)
now = SkTime::GetMSecs();
SkRect clip;
clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155));
if (0) {
canvas->clipRect(clip);
}
if (0) {
SkPath clipPath;
clipPath.addOval(clip);
canvas->clipPath(clipPath);
}
const char* text = "Hamburgefons";
size_t length = strlen(text);
#ifdef TEST_CLICKX
{
SkPaint p;
p.setColor(SK_ColorGREEN);
p.setAntiAlias(true);
canvas->drawLine(fClickX, 0, fClickX, SkIntToScalar(1000), p);
}
#endif
for (int j = 0; j < REPEAT_COUNT; j++)
{
SkScalar y = SkIntToScalar(0);
for (int i = 9; i <= 24; i++) {
paint.setTextSize(SkIntToScalar(i) /*+ (gRand.nextU() & 0xFFFF)*/);
for (SkScalar dx = 0; dx <= SkIntToScalar(3)/4; dx += SkIntToScalar(1) /* /4 */)
{
y += paint.getFontSpacing();
DrawTheText(canvas, text, length, SkIntToScalar(20) + dx, y, paint, fClickX, fMF);
}
}
if (gHints[index].fFlushCache) {
SkGraphics::SetFontCacheUsed(0);
}
}
if (REPEAT_COUNT > 1)
{
printf("--------- FPS = %g\n", REPEAT_COUNT * 1000. / (SkTime::GetMSecs() - now));
this->inval(NULL);
}
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y)
{
fClickX = x;
this->inval(NULL);
return this->INHERITED::onFindClickHandler(x, y);
}
virtual bool onClick(Click* click)
{
return this->INHERITED::onClick(click);
}
private:
int fHints;
SkScalar fClickX;
SkMaskFilter* fMF;
SkShader* fGradient;
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new TextSpeedView; }
static SkViewRegister reg(MyFactory);