Adding my Bitmap2Path sample for 1on1 meeting.

BUG=
R=reed@google.com, borenet@google.com

Author: dierk@google.com

Review URL: https://chromiumcodereview.appspot.com/16829003

git-svn-id: http://skia.googlecode.com/svn/trunk@9843 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2013-07-01 17:50:29 +00:00
parent c6081abd2f
commit 064779aa18
9 changed files with 496 additions and 0 deletions

79
bench/PathUtilsBench.cpp Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBenchmark.h"
#include "SkCanvas.h"
#include "SkPathUtils.h"
#include "SkRandom.h"
#include "SkTime.h"
#include "SkString.h"
#define H 16
#define W 16
#define STRIDE 2
//this function is redefined for sample, test, and bench. is there anywhere
// I can put it to avoid code duplcation?
static void fillRandomBits( int chars, char* bits ){
SkTime time;
SkMWCRandom rand = SkMWCRandom( time.GetMSecs() );
for (int i = 0; i < chars; ++i){
bits[i] = rand.nextU();
}
}
static void path_proc(char* bits, SkPath* path) {
SkPathUtils::BitsToPath_Path(path, bits, H, W, STRIDE);
}
static void region_proc(char* bits, SkPath* path) {
SkPathUtils::BitsToPath_Region(path, bits, H, W, STRIDE);
}
/// Emulates the mix of rects blitted by gmail during scrolling
class PathUtilsBench : public SkBenchmark {
typedef void (*Proc)(char*, SkPath*);
Proc fProc;
int fH, fW, fStride;
SkString fName;
char* bits[H * STRIDE];
enum { N = SkBENCHLOOP(20) };
public:
PathUtilsBench(void* param, Proc proc, const char name[]) : INHERITED(param) {
fProc = proc;
fName.printf("pathUtils_%s", name);
}
protected:
virtual const char* onGetName() { return fName.c_str(); }
virtual void onDraw(SkCanvas* canvas) {
for (int i = 0; i < N; ++i){
//create a random 16x16 bitmap
fillRandomBits(H * STRIDE, (char*) &bits);
//use passed function pointer to handle it
SkPath path;
fProc( (char*) &bits, &path);
}
}
private:
typedef SkBenchmark INHERITED;
};
static SkBenchmark* PU_path(void* p) { return SkNEW_ARGS(PathUtilsBench, (p, path_proc, "path")); }
static SkBenchmark* PU_region(void* p) { return SkNEW_ARGS(PathUtilsBench, (p, region_proc, "region")); }
static BenchRegistry PU_Path(PU_path);
static BenchRegistry PU_Region(PU_region);

View File

@ -86,6 +86,7 @@
'../samplecode/SamplePatch.cpp',
'../samplecode/SamplePath.cpp',
'../samplecode/SamplePathClip.cpp',
'../samplecode/SamplePathUtils.cpp',
'../samplecode/SamplePathEffects.cpp',
'../samplecode/SamplePicture.cpp',
'../samplecode/SamplePictFile.cpp',

View File

@ -44,6 +44,7 @@
'../bench/MutexBench.cpp',
'../bench/PathBench.cpp',
'../bench/PathIterBench.cpp',
'../bench/PathUtilsBench.cpp',
'../bench/PerlinNoiseBench.cpp',
'../bench/PicturePlaybackBench.cpp',
'../bench/PictureRecordBench.cpp',

View File

@ -83,6 +83,7 @@
'../tests/PathCoverageTest.cpp',
'../tests/PathMeasureTest.cpp',
'../tests/PathTest.cpp',
'../tests/PathUtilsTest.cpp',
'../tests/PDFPrimitivesTest.cpp',
'../tests/PictureTest.cpp',
'../tests/PictureUtilsTest.cpp',

View File

@ -12,6 +12,7 @@
'../include/effects',
'../include/images',
'../include/lazy',
'../include/pathops',
'../include/pipe',
'../include/utils',
'../include/utils/mac',
@ -82,6 +83,7 @@
'../src/utils/SkParseColor.cpp',
'../src/utils/SkParsePath.cpp',
'../src/utils/SkPictureUtils.cpp',
'../src/utils/SkPathUtils.cpp',
'../src/utils/SkProxyCanvas.cpp',
'../src/utils/SkSHA1.cpp',
'../src/utils/SkSHA1.h',

View File

@ -0,0 +1,40 @@
/*
* CAUTION: EXPERIMENTAL CODE
*
* This code is not to be used and will not be supported
* if it fails on you. DO NOT USE!
*
*/
#ifndef SkPathUtils_DEFINED
#define SKPathUtils_DEFINED
#include "SkPath.h"
/*
* The following methods return the boundary path given a 1-bit bitmap, specified
* by width/height and stride. The bits are interpreted as 1 being "in" the path,
* and 0 being "out". The bits are interpreted as MSB on the left, and LSB on the right.
*/
class SK_API SkPathUtils {
public:
/**
This variation iterates the binary data sequentially (as in scanline fashion)
and will add each run of 1's to the path as a rectangular path. Upon parsing
all binary data the path is simplified using the PathOps::Simplify() method.
*/
static void BitsToPath_Path(SkPath* path, const char* bitmap,
int h, int w, int stride);
/**
This variation utilizes the SkRegion class to generate paths, adding
each run of 1's to the SkRegion as an SkIRect. Upon parsing the entirety
of the binary the SkRegion is converted to a Path via getBoundaryPath().
*/
static void BitsToPath_Region(SkPath* path, const char* bitmap,
int h, int w, int stride);
};
#endif

View File

@ -0,0 +1,85 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SampleCode.h"
#include "SkCanvas.h"
#include "SkPathUtils.h"
#include "SkView.h"
//#include "SkPathOps.h" // loads fine here, won't in PathUtils src files
#include "SkRandom.h"
#include "SkTime.h"
class samplePathUtils : public SampleView {
public:
samplePathUtils() {
bmp_paint.setAntiAlias(true); // Black paint for bitmap
bmp_paint.setStyle(SkPaint::kFill_Style);
bmp_paint.setColor(SK_ColorBLACK);
}
protected:
static const int numModes = 3;
static const int h=8, w=12, stride=2, scale=10; // stride is in bytes
static const int numChars = h * stride; // number of chars in entire array
SkPaint bmp_paint;
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "PathUtils");
return true;
}
return this->INHERITED::onQuery(evt);
}
/////////////////////////////////////////////////////////////
virtual void onDrawContent(SkCanvas* canvas) {
// bitmap definitions
const char bits[numModes][numChars] = {
{ 0x18, 0x00, 0x3c, 0x00, 0x7e, 0x00, 0xdb, 0x00,
0xff, 0x00, 0x24, 0x00, 0x5a, 0x00, 0xa5, 0x00 },
{ 0x20, 0x80, 0x91, 0x20, 0xbf, 0xa0, 0xee, 0xe0,
0xff, 0xe0, 0x7f, 0xc0, 0x20, 0x80, 0x40, 0x40 },
{ 0x0f, 0x00, 0x7f, 0xe0, 0xff, 0xf0, 0xe6, 0x70,
0xff, 0xf0, 0x19, 0x80, 0x36, 0xc0, 0xc0, 0x30 }
};
for (int i = 0; i < numModes; ++i) {
SkPath path; // generate and simplify each path
SkPathUtils::BitsToPath_Path(&path, (char*) &bits[i], h, w, stride);
canvas->save(); // DRAWING
canvas->scale(scale, scale); // scales up each bitmap
canvas->translate(0, 1.5f * h * i);
canvas->drawPath(path, bmp_paint); // draw bitmap
canvas->restore();
// use the SkRegion method
SkPath pathR;
SkPathUtils::BitsToPath_Region(&pathR, (char*) &bits[i], h, w, stride);
canvas->save();
canvas->scale(scale, scale); // scales up each bitmap
canvas->translate(1.5f * w, 1.5f * h * i); // translates past previous bitmap
canvas->drawPath(pathR, bmp_paint); // draw bitmap
canvas->restore();
}
}
private:
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new samplePathUtils; }
static SkViewRegister reg(MyFactory)
;

150
src/utils/SkPathUtils.cpp Normal file
View File

@ -0,0 +1,150 @@
/*
* CAUTION: EXPERIMENTAL CODE
*
* This code is not to be used and will not be supported
* if it fails on you. DO NOT USE!
*
*/
#include "SkPathUtils.h"
#include "SkPath.h"
#include "SkPathOps.h" // this can't be found, how do I link it?
#include "SkRegion.h"
typedef void (*line2path)(SkPath*, const char*, int, int);
#define SQRT_2 1.41421356237f
#define ON 0xFF000000 // black pixel
#define OFF 0x00000000 // transparent pixel
// assumes stride is in bytes
/*
static void FillRandomBits( int chars, char* bits ){
SkTime time;
SkMWCRandom rand = SkMWCRandom( time.GetMSecs() );
for (int i = 0; i < chars; ++i){
bits[i] = rand.nextU();
}
}OA
*/
static int GetBit( const char* buffer, int x ) {
int byte = x >> 3;
int bit = x & 7;
return buffer[byte] & (128 >> bit);
}
/*
static void Line2path_pixel(SkPath* path, const char* line,
int lineIdx, int width) {
for (int i = 0; i < width; ++i) {
// simply makes every ON pixel into a rect path
if (GetBit(line,i)) {
path->addRect(SkRect::MakeXYWH(i, lineIdx, 1, 1),
SkPath::kCW_Direction);
}
}
}
static void Line2path_pixelCircle(SkPath* path, const char* line,
int lineIdx, int width) {
for (int i = 0; i < width; ++i) {
// simply makes every ON pixel into a circle path
if (GetBit(line,i)) {
path->addCircle(i + SK_ScalarHalf,
lineIdx + SK_ScalarHalf,
SkFloatToScalar(SQRT_2 / 2.0f));
}
}
}
*/
static void Line2path_span(SkPath* path, const char* line,
int lineIdx, int width) {
bool inRun = 0;
int start = 1;
for (int i = 0; i < width; ++i) {
int curPixel = GetBit(line,i);
if ( (curPixel!=0) != inRun ) { // if transition
if (curPixel) { // if transition on
inRun = 1;
start = i; // mark beginning of span
}else { // if transition off add the span as a path
inRun = 0;
path->addRect(SkRect::MakeXYWH(start, lineIdx, i-start, 1),
SkPath::kCW_Direction);
}
}
}
if (inRun==1) { // close any open spans
int end = 0;
if ( GetBit(line,width-1) ) ++end;
path->addRect(SkRect::MakeXYWH(start, lineIdx,
width - 1 + end - start, 1),
SkPath::kCW_Direction);
} else if ( GetBit(line,width-1) ) { // if last pixel on add rect
path->addRect(SkRect::MakeXYWH(width-1, lineIdx, 1, 1),
SkPath::kCW_Direction);
}
}
void SkPathUtils::BitsToPath_Path(SkPath* path,
const char* bitmap,
int h, int w, int stride) {
// loop for every line in bitmap
for (int i = 0; i < h; ++i) {
// fn ptr handles each line separately
//l2p_fn(path, &bitmap[i*stride], i, w);
Line2path_span(path, &bitmap[i*stride], i, w);
}
Simplify(*path, path); // simplify resulting bitmap
}
void SkPathUtils::BitsToPath_Region(SkPath* path,
const char* bitmap,
int h, int w, int stride) {
SkRegion region;
// loop for each line
for (int y = 0; y < h; ++y){
bool inRun = 0;
int start = 1;
const char* line = &bitmap[y * stride];
// loop for each pixel
for (int i = 0; i < w; ++i) {
int curPixel = GetBit(line,i);
if ( (curPixel!=0) != inRun ) { // if transition
if (curPixel) { // if transition on
inRun = 1;
start = i; // mark beginning of span
}else { // if transition off add the span as a path
inRun = 0;
//add here
region.op(SkIRect::MakeXYWH(start, y, i-start, 1),
SkRegion::kUnion_Op );
}
}
}
if (inRun==1) { // close any open spans
int end = 0;
if ( GetBit(line,w-1) ) ++end;
// add the thing here
region.op(SkIRect::MakeXYWH(start, y, w-1-start+end, 1),
SkRegion::kUnion_Op );
} else if ( GetBit(line,w-1) ) { // if last pixel on add rect
// add the thing here
region.op(SkIRect::MakeXYWH(w-1, y, 1, 1),
SkRegion::kUnion_Op );
}
}
// convert region to path
region.getBoundaryPath(path);
}

137
tests/PathUtilsTest.cpp Normal file
View File

@ -0,0 +1,137 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkPathUtils.h"
#include "SkRandom.h"
#include "SkTime.h"
#define NUM_IT 100000
#define ON 0xFF000000 // black pixel
#define OFF 0x00000000 // transparent pixel
class SkBitmap;
//this function is redefined for sample, test, and bench. is there anywhere
// I can put it to avoid code duplcation?
static void fillRandomBits( int chars, char* bits ){
SkTime time;
SkMWCRandom rand = SkMWCRandom( time.GetMSecs() );
for (int i = 0; i < chars; ++i){
bits[i] = rand.nextU();
}
}
//also defined within PathUtils.cpp, but not in scope here. Anyway to call it
// without re-defining it?
static int getBit( const char* buffer, int x ) {
int byte = x >> 3;
int bit = x & 7;
return buffer[byte] & (1 << bit);
}
static void bin2SkBitmap(const char* bin_bmp, SkBitmap* sk_bmp,
int h, int w, int stride){
//init the SkBitmap
sk_bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
sk_bmp->allocPixels();
for (int y = 0; y < h; ++y) { // for every row
const char* curLine = &bin_bmp[y * stride];
for (int x = 0; x < w; ++x) {// for every pixel
if (getBit(curLine, x)) {
*sk_bmp->getAddr32(x,y) = ON;
}
else {
*sk_bmp->getAddr32(x,y) = OFF;
}
}
}
}
static bool test_bmp(skiatest::Reporter* reporter,
const SkBitmap* bmp1, const SkBitmap* bmp2,
int h, int w) {
for (int y = 0; y < h; ++y) { // loop through all pixels
for (int x = 0; x < w; ++x) {
REPORTER_ASSERT( reporter, *bmp1->getAddr32(x,y) == *bmp1->getAddr32(x,y) );
}
}
return true;
}
static void test_path_eq(skiatest::Reporter* reporter, const SkPath* path,
const SkBitmap* truth, int h, int w){
// make paint
SkPaint bmpPaint;
bmpPaint.setAntiAlias(true); // Black paint for bitmap
bmpPaint.setStyle(SkPaint::kFill_Style);
bmpPaint.setColor(SK_ColorBLACK);
// make bmp
SkBitmap bmp;
bmp.setConfig(SkBitmap::kARGB_8888_Config, w, h);
bmp.allocPixels();
SkCanvas(bmp).drawPath(*path, bmpPaint);
// test bmp
test_bmp(reporter, &bmp, truth, h, w);
}
static void test_path(skiatest::Reporter* reporter, const SkBitmap* truth,
const char* bin_bmp, int h, int w, int stride){
// make path
SkPath path;
SkPathUtils::BitsToPath_Path(&path, bin_bmp, h, w, stride);
//test for correctness
test_path_eq(reporter, &path, truth, h, w);
}
static void test_region(skiatest::Reporter* reporter, const SkBitmap* truth,
const char* bin_bmp, int h, int w, int stride){
//generate bitmap
SkPath path;
SkPathUtils::BitsToPath_Region(&path, bin_bmp, h, w, stride);
//test for correctness
test_path_eq(reporter, &path, truth, h, w);
}
static void TestPathUtils(skiatest::Reporter* reporter) {
const int w[4] = {4, 8, 12, 16};
int h = 8, stride = 4;
char bits[ h * stride ];
static char* bin_bmp = &bits[0];
//loop to run randomized test lots of times
for (int it = 0; it < NUM_IT; ++it)
{
// generate a random binary bitmap
fillRandomBits( h * stride, bin_bmp); // generate random bitmap
// for each bitmap width, use subset of binary bitmap
for (int i = 0; i < 4; ++i) {
// generate truth bitmap
SkBitmap bmpTruth;
bin2SkBitmap(bin_bmp, &bmpTruth, h, w[i], stride);
test_path(reporter, &bmpTruth, bin_bmp, h, w[i], stride);
test_region(reporter, &bmpTruth, bin_bmp, h, w[i], stride);
}
}
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("PathUtils", PathUtils, TestPathUtils)