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:
parent
c6081abd2f
commit
064779aa18
79
bench/PathUtilsBench.cpp
Normal file
79
bench/PathUtilsBench.cpp
Normal 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);
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
40
include/utils/SkPathUtils.h
Normal file
40
include/utils/SkPathUtils.h
Normal 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
|
85
samplecode/SamplePathUtils.cpp
Normal file
85
samplecode/SamplePathUtils.cpp
Normal 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
150
src/utils/SkPathUtils.cpp
Normal 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
137
tests/PathUtilsTest.cpp
Normal 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)
|
Loading…
Reference in New Issue
Block a user