Saturday, May 2, 2015

How to write a program using Skia on Windows

Skia is an open source 2D graphics library which provides common APIs that work across a variety of hardware and software platforms. It serves as the graphics engine for Google Chrome and Chrome OS, Android, Mozilla Firefox and Firefox OS, and many other products. Skia is an alternative to the Cairo library.

Posting this in case it helps anyone else.

Prerequisites:
Visual Studio 2013 (including the express or community editions, which are free)
Unzipping tool like 7zip, WinRAR

The command prompt lines below should be run in the same session (i.e. it won't work if you close and reopen a new command prompt).

  • Download depot_tools.zip from the Install Depot Tools page
  • Use 7zip or WinRAR to Extract All to a path like c:\path\to\depot_tools (no spaces in path). The Windows built-in unzip ight skip hidden files.
  • Open a command prompt
  • Run "cd c:\path\to\depot_tools"
  • Run "echo %PATH%"
  • In the output, if you already have Python installed and see a Python directory, you might want to remove this from the path. set PATH=x can do this for just this command session.
  • In the output, if you already have Git installed and see a Git directory, you might want to remove this from the path. set PATH=x can do this for just this command session.
  • Run "set PATH=%PATH%;c:\path\to\depot_tools" to add depot tools to the path
  • Run "gclient". This will download and sync the needed tools.
  • Make a directory like c:\path\to\skia (no spaces in path)
  • In the same command prompt Run "cd c:\path\to\skia"
  • Run git config --global user.name "Your Name"
  • Run git config --global user.email you@example.com
  • mkdir skia
  • cd skia
  • gclient config --name . --unmanaged https://skia.googlesource.com/skia.git
  • gclient sync
  • git checkout master
  • Run "set GYP_GENERATORS=msvs"
  • Run "python gyp_skia"
  • Run "ren out out86"
  • Run "python gyp_skia -D skia_arch_width=64"
  • Run "ren out out64"
  • Open .\out86\skia.sln in Visual Studio
  • For me, I only needed to build Release
  • For me, I didn't need these projects, and also these failed to build as they couldn't find QT. Open Configuration Manager, under the Debug/Release drop down, uncheck Build for the following debugger, debugger_qt_mocs, pdfviewer, pdfviewer_lib
  • Hit Build Solution, and wait several minutes
  • When the build is done, you may see some compilation warnings/errors but if the default project HelloWorld runs correctly, (Ctrl+F5), it's likely that all of the important parts work.
  • Open .\out64\skia.sln in VS
  • Repeat the above steps for x64.
Now, to create an example project that doesn't need Google's gyp system:
  • Open Visual Studio and create a new project. Other languages > Visual C++ > Win32 > Win32 Console Application
  • In the Win32 Application Wizard, click Application Settings, uncheck Precompiled Header, check Empty Project.
  • Switch from Debug to Release
  • Go into the project's options, Configuration Properties > C/C++ > General > Additional Include Diretories and add: c:\path\to\skia\include\core;c:\path\to\skia\include\config
  • Go into the project's options, Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions and add:
    WIN32
    NDEBUG
    _CONSOLE
    _LIB
    SK_INTERNAL
    SK_GAMMA_SRGB
    SK_GAMMA_APPLY_TO_A8
    SK_SCALAR_TO_FLOAT_EXCLUDED
    SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1
    SK_SUPPORT_GPU=1
    SK_SUPPORT_OPENCL=0
    SK_FORCE_DISTANCE_FIELD_TEXT=0
    SK_BUILD_FOR_WIN32
    GR_GL_FUNCTION_TYPE=__stdcall
    SK_DEVELOPER=1
    
  • Go into the project's options, Configuration Properties > Linker > Input > Additional Dependencies and add (preferably as relative paths)
    c:\path\to\skia\out86\Release\skia_core.lib
    c:\path\to\skia\out86\Release\skia_effects.lib
    c:\path\to\skia\out86\Release\skia_images.lib
    c:\path\to\skia\out86\Release\skia_opts.lib
    c:\path\to\skia\out86\Release\skia_ports.lib
    c:\path\to\skia\out86\Release\skia_sfnt.lib
    c:\path\to\skia\out86\Release\skia_utils.lib
    c:\path\to\skia\out86\Release\skia_skgpu.lib
    c:\path\to\skia\out86\Release\skia_opts_sse41.lib
    c:\path\to\skia\out86\Release\skia_opts_ssse3.lib
    c:\path\to\skia\out86\Release\lib\libetc1.lib
    c:\path\to\skia\out86\Release\lib\libSkKTX.lib 
Then, add a main.cpp to the project, with the following code,
#include <string>
#include <fstream>

#include "SkCanvas.h"
#include "SkData.h"
#include "SkDocument.h"
#include "SkGraphics.h"
#include "SkSurface.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkString.h"

#include "..\effects\SkGradientShader.h"

void save_ppm(SkBitmap const& bitmap, std::string const& filename)
{
  SkAutoLockPixels l(bitmap);

  std::ofstream ofile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
  if (ofile.is_open())
  {
    ofile << "P6 " << bitmap.width() << " " << bitmap.height() << " 255 ";

    for (int i = 0; i != bitmap.height(); i++)
    {
      for (int j = 0; j != bitmap.width(); j++)
      {
        SkColor const* c = bitmap.getAddr32(j, i);
        char buf[3] = { SkColorGetR(*c), SkColorGetG(*c), SkColorGetB(*c) };
        ofile.write(buf, 3);
      }
    }
  }
}

void TestSkia(SkCanvas& canvas)
{
  SkPaint paint;
  paint.setAntiAlias(true);
  paint.setColor(SK_ColorRED);
  SkRect rect = {
    20, 20,
    50, 50
  };
  canvas.drawRect(rect, paint);
}

int main(int argc, char * const argv[])
{
  SkAutoGraphics ag;
  SkBitmap bitmap;
  int width = 800;
  int height = 600;
  bitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height));
  SkCanvas canvas(bitmap);
  canvas.drawColor(SK_ColorWHITE);

  TestSkia(canvas);

  save_ppm(bitmap, "out.ppm");
  return 0;
}

// stub out openGl dependency, which isn't needed in this case.
extern "C"
{
#ifdef _WIN64
  PROC WINAPI __imp_wglGetProcAddress(LPCSTR)
  {
    abort();
    return nullptr;
  }
  
  HGLRC WINAPI  __imp_wglGetCurrentContext()
  {
    abort();
    return nullptr;
  }
#else
  PROC WINAPI _imp__wglGetProcAddress(LPCSTR)
  {
    abort();
    return nullptr;
  }

  HGLRC WINAPI _imp__wglGetCurrentContext()
  {
    abort();
    return nullptr;
  }
#endif
}


Running this little program will create a valid ppm file with a red rectangle!



To build for x64, you can create a new x64 target and update the lib directories from c:\path\to\skia\out86 to c:\path\to\skia\out64.

To add codecs for saving to different image types:
  • In Linker Inputs, add a reference to skia_codecs.lib
  • Add #include "..\images\SkForceLinking.h"
  • add the line __SK_FORCE_IMAGE_DECODER_LINKING;


To add OpenGL:
  • remove the __imp_wglGetProcAddress and __imp_wglGetCurrentContext stubs
  • In Linker Inputs, add references to the following:
    OpenGL32.lib
    usp10.lib
    kernel32.lib
    gdi32.lib
    winspool.lib
    comdlg32.lib
    advapi32.lib
    shell32.lib
    ole32.lib
    oleaut32.lib
    user32.lib
    uuid.lib
    odbc32.lib
    odbccp32.lib
    DelayImp.lib
    windowscodecs.lib


Sources:
Install Depot Tools
Skia Quick Start Guides Windows

2 comments:

None said...

Thank you man!

tuệ việt trí said...

Hi, I am very interested in your article! But now SKia have many code update so, help me build it in latest version! please update more video. Thanks you very much!