Coding Style Guidelines

These conventions have evolved over time. Some of the earlier code in both projects doesn’t strictly adhere to the guidelines. However, as the code evolves we hope to make the existing code conform to the guildelines.

Files

We use .cpp and .h as extensions for c++ source and header files.

Headers that aren’t meant for public consumption should be placed in src directories so that they aren’t in a client’s search path, or in include/private if they need to be used by public headers.

We prefer to minimize includes. If forward declaring a name in a header is sufficient then that is preferred to an include.

Forward declarations and file includes should be in alphabetical order.

No define before sktypes

Do not use #if/#ifdef before including “SkTypes.h” (directly or indirectly). Most things you’d #if on tend to not yet be decided until SkTypes.h.

We use 4 spaces, not tabs.

We use Unix style endlines (LF).

We prefer no trailing whitespace but aren’t very strict about it.

We wrap lines at 100 columns unless it is excessively ugly (use your judgement).

Naming

Most externally visible types and functions use an Sk- prefix to designate they’re part of Skia, but code in Ganesh uses Gr-. Nested types need not be prefixed.

class SkClass {
public:
    class HelperClass {
        ...
    };
};

Data fields in structs, classes, and unions that have methods begin with lower-case f and are then camel-capped, to distinguish those fields from other variables. Types that are predominantly meant for direct field access don’t need f-decoration.

struct GrCar {
    float milesDriven;
    Color color;
};

class GrMotorcyle {
public:
    float getMilesDriven() const { return fMilesDriven; }
    void  setMilesDriven(float milesDriven) { fMilesDriven = milesDriven; }

    Color getColor() const { return fColor; }
private:
    float fMilesDriven;
    Color fColor;
};

Global variables are similar but prefixed with g and camel-capped.

bool gLoggingEnabled;

Local variables and arguments are camel-capped with no initial cap.

int herdCats(const Array& cats) {
    int numCats = cats.count();
}

Variables declared constexpr or const, and whose value is fixed for the duration of the program, are named with a leading “k” and then camel-capped.

int drawPicture() {
    constexpr SkISize kPictureSize = {100, 100};
    constexpr float kZoom = 1.0f;
}

Enum values are also prefixed with k. Unscoped enum values are postfixed with an underscore and singular name of the enum name. The enum itself should be singular for exclusive values or plural for a bitfield. If a count is needed it is k<singular enum name>Count and not be a member of the enum (see example), or a kLast member of the enum is fine too.

// Enum class does not need suffixes.
enum class SkPancakeType {
     kBlueberry,
     kPlain,
     kChocolateChip,
};
// Enum should have a suffix after the enum name.
enum SkDonutType {
     kGlazed_DonutType,
     kSprinkles_DonutType,
     kChocolate_DonutType,
     kMaple_DonutType,

     kLast_DonutType = kMaple_DonutType
};

static const SkDonutType kDonutTypeCount = kLast_DonutType + 1;
enum SkSausageIngredientBits {
    kFennel_SausageIngredientBit = 0x1,
    kBeef_SausageIngredientBit   = 0x2
};
enum SkMatrixFlags {
    kTranslate_MatrixFlag = 0x1,
    kRotate_MatrixFlag    = 0x2
};

Macros are all caps with underscores between words. Macros that have greater than file scope should be prefixed SK or GR.

Static non-class functions in implementation files are lower-case with underscores separating words:

static inline bool tastes_like_chicken(Food food) {
    return kIceCream_Food != food;
}

Externed functions or static class functions are camel-capped with an initial cap:

bool SkIsOdd(int n);

class SkFoo {
public:
    static int FooInstanceCount();

    // Not static.
    int barBaz();
};

Macros

Ganesh macros that are GL-specific should be prefixed GR_GL.

#define GR_GL_TEXTURE0 0xdeadbeef

Ganesh prefers that macros are always defined and the use of #if MACRO rather than #ifdef MACRO.

#define GR_GO_SLOWER 0
...
#if GR_GO_SLOWER
    Sleep(1000);
#endif

The rest of Skia tends to use #ifdef SK_MACRO for boolean flags.

Braces

Open braces don’t get a newline. else and else if appear on same line as opening and closing braces unless preprocessor conditional compilation interferes. Braces are always used with if, else, while, for, and do.

if (...) {
    oneOrManyLines;
}

if (...) {
    oneOrManyLines;
} else if (...) {
    oneOrManyLines;
} else {
    oneOrManyLines;
}

for (...) {
    oneOrManyLines;
}

while (...) {
    oneOrManyLines;
}

void function(...) {
    oneOrManyLines;
}

if (!error) {
    proceed_as_usual();
}
#if HANDLE_ERROR
else {
    freak_out();
}
#endif

Flow Control

There is a space between flow control words and parentheses, and between parentheses and braces:

while (...) {
}

do {
} while (...);

switch (...) {
...
}

Cases and default in switch statements are indented from the switch.

switch (color) {
    case kBlue:
        ...
        break;
    case kGreen:
        ...
        break;
    ...
    default:
       ...
       break;
}

Fallthrough from one case to the next is annotated with [[fallthrough]]. However, when multiple case statements in a row are used, they do not need the [[fallthrough]] annotation.

switch (recipe) {
    ...
    case kSmallCheesePizza_Recipe:
    case kLargeCheesePizza_Recipe:
        ingredients |= kCheese_Ingredient | kDough_Ingredient | kSauce_Ingredient;
        break;
    case kCheeseOmelette_Recipe:
        ingredients |= kCheese_Ingredient;
        [[fallthrough]]
    case kPlainOmelette_Recipe:
        ingredients |= (kEgg_Ingredient | kMilk_Ingredient);
        break;
    ...
}

When a block is needed to declare variables within a case follow this pattern:

switch (filter) {
    ...
    case kGaussian_Filter: {
        Bitmap srcCopy = src->makeCopy();
        ...
    } break;
    ...
};

Classes

Unless there is a need for forward declaring something, class declarations should be ordered public, protected, private. Each should be preceded by a newline. Within each visibility section (public, private), fields should not be intermixed with methods. It’s nice to keep all data fields together at the end.

class SkFoo {

public:
    ...

protected:
    ...

private:
    void barHelper(...);
    ...

    SkBar fBar;
    ...
};

Virtual functions that are overridden in derived classes should use override, and the virtual keyword should be omitted.

void myVirtual() override {
}

If you call a method on a parent type that must stand out as specifically the parent’s version of that method, such as Parent::method(). The this-> that would normally be required before a method call on the current object is not necessary when using a scope qualifier.

class GrDillPickle : public GrPickle {
    ...
    bool onTasty() const override {
        return GrPickle::onTasty()
            && fFreshDill;
    }
    ...
private:
    bool fFreshDill;
};

Constructor initializers should be placed on the same line as the constructor, if they fit. Otherwise, each initializer should be on its own line, indented, with punctuation placed before the initializer.

GrDillPickle::GrDillPickle() : GrPickle(), fSize(kDefaultPickleSize) {}

GrDillPickle::GrDillPickle(float size, float crunchiness, const PickleOptions* options)
        : GrPickle(options)
        , fSize(size)
        , fCrunchiness(crunchiness) {}

Constructors that take one argument should almost always be explicit, with exceptions made only for the (rare) automatic compatibility class.

class Foo {
    explicit Foo(int x);  // Good.
    Foo(float y);         // Spooky implicit conversion from float to Foo.  No no no!
    ...
};

Method calls within method calls should be prefixed with dereference of the ‘this’ pointer. For example:

this->method();

A common pattern for virtual methods in Skia is to include a public non-virtual (or final) method, paired with a private virtual method named “onMethodName”. This ensures that the base-class method is always invoked and gives it control over how the virtual method is used, rather than relying on each subclass to call Parent::onMethodName(). For example:

class SkSandwich {
public:
    void assemble() {
        // All sandwiches must have bread on the top and bottom.
        this->addIngredient(kBread_Ingredient);
        this->onAssemble();
        this->addIngredient(kBread_Ingredient);
    }
    bool cook() {
        return this->onCook();
    }

private:
    // All sandwiches must implement onAssemble.
    virtual void onAssemble() = 0;
    // Sandwiches can remain uncooked by default.
    virtual bool onCook() { return true; }
};

class SkGrilledCheese : public SkSandwich {
private:
    void onAssemble() override {
        this->addIngredient(kCheese_Ingredient);
    }
    bool onCook() override {
        return this->toastOnGriddle();
    }
};

class SkPeanutButterAndJelly : public SkSandwich {
private:
    void onAssemble() override {
        this->addIngredient(kPeanutButter_Ingredient);
        this->addIngredient(kGrapeJelly_Ingredient);
    }
};

Integer Types

We follow the Google C++ guide for ints and are slowly making older code conform to this

(https://google.github.io/styleguide/cppguide.html#Integer_Types)

Summary: Use int unless you have need a guarantee on the bit count, then use stdint.h types (int32_t, etc). Assert that counts, etc are not negative instead of using unsigned. Bitfields use uint32_t unless they have to be made shorter for packing or performance reasons.

Function Parameters

Mandatory constant object parameters are passed to functions as const references. Optional constant object parameters are passed to functions as const pointers. Mutable object parameters are passed to functions as pointers. We very rarely pass anything by non-const reference.

// src and paint are optional
void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
                              const SkRect& dst, const SkPaint* paint = nullptr);

// metrics is mutable (it is changed by the method)
SkScalar SkPaint::getFontMetrics(FontMetric* metrics, SkScalar scale) const;

If function arguments or parameters do not all fit on one line, the overflowing parameters may be lined up with the first parameter on the next line

void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst,
                    const SkPaint* paint = nullptr) {
    this->drawBitmapRectToRect(bitmap, nullptr, dst, paint,
                               kNone_DrawBitmapRectFlag);
}

or all parameters placed on the next line and indented eight spaces

void drawBitmapRect(
        const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint = nullptr) {
    this->drawBitmapRectToRect(
            bitmap, nullptr, dst, paint, kNone_DrawBitmapRectFlag);
}

Python

Python code follows the Google Python Style Guide.

Folder Organiziation

Skia’s public API should live in the include directory. Skia’s private headers and implementation files should live in the src directory. The modules directory contains extra features that are built on top of Skia (modules/skcms being an exception) and can be used by clients. Private utilities to test Skia live in tools and can be used for reference but should not be used by clients.

No header in include should depend on files in other directories (modules/skcms being an exception). This is to prevent private symbols from leaking into client code via transitive dependencies.