iPlug 2: IGraphicsSkia.cpp Source File

1#include <cmath>

2#include <map>

3

4#include "IGraphicsSkia.h"

5

6#pragma warning( push )

7#pragma warning( disable : 4244 )

8#include "include/core/SkMaskFilter.h"

9#include "include/core/SkBlurTypes.h"

10#include "include/core/SkFont.h"

11#include "include/core/SkFontMetrics.h"

12#include "include/core/SkTypeface.h"

13#include "include/core/SkVertices.h"

14#include "include/core/SkSwizzle.h"

15#include "include/core/SkBitmap.h"

16#include "include/core/SkFontMgr.h"

17#include "include/core/SkPathEffect.h"

18#include "include/effects/SkDashPathEffect.h"

19#include "include/effects/SkGradientShader.h"

20#include "include/effects/SkImageFilters.h"

21

22#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH

23#include "modules/skparagraph/include/FontCollection.h"

24#include "modules/skparagraph/include/Paragraph.h"

25#include "modules/skparagraph/include/ParagraphBuilder.h"

26#include "modules/skparagraph/include/ParagraphStyle.h"

27#include "modules/skparagraph/include/TextStyle.h"

28#include "modules/skshaper/include/SkShaper.h"

29#include "modules/skunicode/include/SkUnicode_icu.h"

30#endif

31#pragma warning( pop )

32#include "include/gpu/ganesh/SkSurfaceGanesh.h"

33#include "include/gpu/GrBackendSurface.h"

34

35#if defined OS_MAC || defined OS_IOS

36 #include "include/utils/mac/SkCGUtils.h"

37 #if defined IGRAPHICS_GL2

38 #error SKIA doesn't work correctly with IGRAPHICS_GL2

39 #elif defined IGRAPHICS_GL3

40 #include <OpenGL/gl3.h>

41 #elif defined IGRAPHICS_METAL

42

43 #import <Metal/Metal.h>

44 #import <QuartzCore/CAMetalLayer.h>

45 #include "include/gpu/ganesh/mtl/GrMtlBackendContext.h"

46 #include "include/gpu/ganesh/mtl/GrMtlDirectContext.h"

47 #include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h"

48 #elif !defined IGRAPHICS_CPU

49 #error Define either IGRAPHICS_GL2, IGRAPHICS_GL3, IGRAPHICS_METAL, or IGRAPHICS_CPU for IGRAPHICS_SKIA with OS_MAC

50 #endif

51

52 #include "include/ports/SkFontMgr_mac_ct.h"

53

54#elif defined OS_WIN

55 #include "include/ports/SkTypeface_win.h"

56

57 #pragma comment(lib, "skia.lib")

58

59 #ifndef IGRAPHICS_NO_SKIA_SKPARAGRAPH

60 #pragma comment(lib, "skparagraph.lib")

61 #pragma comment(lib, "skshaper.lib")

62 #pragma comment(lib, "skunicode_core.lib")

63 #pragma comment(lib, "skunicode_icu.lib")

64 #endif

65

66

67#endif

68

69#if defined IGRAPHICS_GL

70 #include "include/gpu/gl/GrGLInterface.h"

71 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"

72 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"

73

74 #if defined OS_MAC

75 #include "include/gpu/ganesh/gl/mac/GrGLMakeMacInterface.h"

76 #elif defined OS_WIN

77 #include "include/gpu/ganesh/gl/win/GrGLMakeWinInterface.h"

78 #pragma comment(lib, "opengl32.lib")

79 #endif

80

81#endif

82

83using namespace iplug;

84using namespace igraphics;

85

86extern std::map<std::string, MTLTexturePtr> gTextureMap;

87

88#pragma mark - Private Classes and Structs

89

91{

92public:

93 Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale);

94 Bitmap(const char* path, double sourceScale);

95 Bitmap(const void* pData, int size, double sourceScale);

96 Bitmap(sk_sp<SkImage>, double sourceScale);

97

98private:

99 SkiaDrawable mDrawable;

100};

101

102IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale)

103{

104 mDrawable.mSurface = surface;

105 mDrawable.mIsSurface = true;

106

107 SetBitmap(&mDrawable, width, height, scale, drawScale);

108}

109

110IGraphicsSkia::Bitmap::Bitmap(const char* path, double sourceScale)

111{

112 sk_sp<SkData> data = SkData::MakeFromFileName(path);

113

114 assert(data && "Unable to load file at path");

115

116 auto image = SkImages::DeferredFromEncodedData(data);

117

118#ifdef IGRAPHICS_CPU

119 image = image->makeRasterImage();

120#endif

121

122 mDrawable.mImage = image;

123

124 mDrawable.mIsSurface = false;

125 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);

126}

127

128IGraphicsSkia::Bitmap::Bitmap(const void* pData, int size, double sourceScale)

129{

130 auto data = SkData::MakeWithoutCopy(pData, size);

131 auto image = SkImages::DeferredFromEncodedData(data);

132

133#ifdef IGRAPHICS_CPU

134 image = image->makeRasterImage();

135#endif

136

137 mDrawable.mImage = image;

138

139 mDrawable.mIsSurface = false;

140 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);

141}

142

143IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkImage> image, double sourceScale)

144{

145#ifdef IGRAPHICS_CPU

146 mDrawable.mImage = image->makeRasterImage();

147#else

148 mDrawable.mImage = image;

149#endif

150

151 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);

152}

153

154struct IGraphicsSkia::Font

155{

156 Font(IFontDataPtr&& data, sk_sp<SkTypeface> typeFace)

157 : mData(std::move(data)), mTypeface(typeFace) {}

158

159 IFontDataPtr mData;

160 sk_sp<SkTypeface> mTypeface;

161};

162

163

164StaticStorage<IGraphicsSkia::Font> IGraphicsSkia::sFontCache;

165

166#pragma mark - Utility conversions

167

168BEGIN_IPLUG_NAMESPACE

169BEGIN_IGRAPHICS_NAMESPACE

170

171SkColor SkiaColor(const IColor& color, const IBlend* pBlend)

172{

173 if (pBlend)

174 return SkColorSetARGB(Clip(static_cast<int>(pBlend->mWeight * color.A), 0, 255), color.R, color.G, color.B);

175 else

176 return SkColorSetARGB(color.A, color.R, color.G, color.B);

177}

178

179SkRect SkiaRect(const IRECT& r)

180{

181 return SkRect::MakeLTRB(r.L, r.T, r.R, r.B);

182}

183

184SkBlendMode SkiaBlendMode(const IBlend* pBlend)

185{

186 if (!pBlend)

187 return SkBlendMode::kSrcOver;

188

189 switch (pBlend->mMethod)

190 {

191 case EBlend::SrcOver: return SkBlendMode::kSrcOver;

192 case EBlend::SrcIn: return SkBlendMode::kSrcIn;

193 case EBlend::SrcOut: return SkBlendMode::kSrcOut;

194 case EBlend::SrcAtop: return SkBlendMode::kSrcATop;

195 case EBlend::DstOver: return SkBlendMode::kDstOver;

196 case EBlend::DstIn: return SkBlendMode::kDstIn;

197 case EBlend::DstOut: return SkBlendMode::kDstOut;

198 case EBlend::DstAtop: return SkBlendMode::kDstATop;

199 case EBlend::Add: return SkBlendMode::kPlus;

200 case EBlend::XOR: return SkBlendMode::kXor;

201 }

202

203 return SkBlendMode::kClear;

204}

205

206SkTileMode SkiaTileMode(const IPattern& pattern)

207{

208 switch (pattern.mExtend)

209 {

210 case EPatternExtend::None: return SkTileMode::kDecal;

211 case EPatternExtend::Reflect: return SkTileMode::kMirror;

212 case EPatternExtend::Repeat: return SkTileMode::kRepeat;

213 case EPatternExtend::Pad: return SkTileMode::kClamp;

214 }

215

216 return SkTileMode::kClamp;

217}

218

219SkPaint SkiaPaint(const IPattern& pattern, const IBlend* pBlend)

220{

221 SkPaint paint;

222 paint.setAntiAlias(true);

223 paint.setBlendMode(SkiaBlendMode(pBlend));

224 int numStops = pattern.NStops();

225

226 if (pattern.mType == EPatternType::Solid || numStops < 2)

227 {

228 paint.setColor(SkiaColor(pattern.GetStop(0).mColor, pBlend));

229 }

230 else

231 {

232 double x1 = 0.0;

233 double y1 = 0.0;

234 double x2 = 0.0;

235 double y2 = 1.0;

236

237 IMatrix m = pattern.mTransform;

241

242 SkPoint points[2] =

243 {

244 SkPoint::Make(x1, y1),

245 SkPoint::Make(x2, y2)

246 };

247

248 SkColor colors[8];

249 SkScalar positions[8];

250

251 assert(numStops <= 8);

252

253 for(int i = 0; i < numStops; i++)

254 {

256 colors[i] = SkiaColor(stop.mColor, pBlend);

257 positions[i] = stop.mOffset;

258 }

259

260 switch (pattern.mType)

261 {

262 case EPatternType::Linear:

263 paint.setShader(SkGradientShader::MakeLinear(points, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));

264 break;

265

266 case EPatternType::Radial:

267 {

268 float xd = points[0].x() - points[1].x();

269 float yd = points[0].y() - points[1].y();

270 float radius = std::sqrt(xd * xd + yd * yd);

271 paint.setShader(SkGradientShader::MakeRadial(points[0], radius, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));

272 break;

273 }

274

275 case EPatternType::Sweep:

276 {

277 SkMatrix matrix = SkMatrix::MakeAll(m.mXX, m.mYX, 0, m.mXY, m.mYY, 0, 0, 0, 1);

278

279 paint.setShader(SkGradientShader::MakeSweep(x1, y1, colors, nullptr, numStops, SkTileMode::kDecal,

280 0, 360 * positions[numStops - 1], 0, &matrix));

281

282 break;

283 }

284

285 default:

286 break;

287 }

288 }

289

290 return paint;

291}

292

293END_IGRAPHICS_NAMESPACE

294END_IPLUG_NAMESPACE

295

296#pragma mark -

297

298static sk_sp<SkFontMgr> SFontMgrFactory()

299{

300#if defined OS_MAC || defined OS_IOS

301 return SkFontMgr_New_CoreText(nullptr);

302#elif defined OS_WIN

303 return SkFontMgr_New_DirectWrite();

304#else

305 #error "Not supported"

306#endif

307}

308

309bool gFontMgrFactoryCreated = false;

310

311sk_sp<SkFontMgr> SkFontMgrRefDefault()

312{

313 static std::once_flag flag;

314 static sk_sp<SkFontMgr> mgr;

315 std::call_once(flag, [] {

316 mgr = SFontMgrFactory();

317 gFontMgrFactoryCreated = true;

318 });

319 return mgr;

320}

321

322#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH

323

324bool gSkUnicodeCreated = false;

325

326sk_sp<SkUnicode> GetUnicode()

327{

328 static std::once_flag flag;

329 static sk_sp<SkUnicode> unicode;

330 std::call_once(flag, [] {

331 unicode = SkUnicodes::ICU::Make();

332 gSkUnicodeCreated = true;

333 });

334

335 if (!unicode)

336 {

337 DBGMSG("Could not load unicode data\n");

338 return nullptr;

339 }

340

341 return unicode;

342}

343#endif

344

345IGraphicsSkia::IGraphicsSkia(IGEditorDelegate& dlg, int w, int h, int fps, float scale)

346: IGraphics(dlg, w, h, fps, scale)

347{

348 mMainPath.setIsVolatile(true);

349

350#if defined IGRAPHICS_CPU

351 DBGMSG("IGraphics Skia CPU @ %i FPS\n", fps);

352#elif defined IGRAPHICS_METAL

353 DBGMSG("IGraphics Skia METAL @ %i FPS\n", fps);

354#elif defined IGRAPHICS_GL

355 DBGMSG("IGraphics Skia GL @ %i FPS\n", fps);

356#endif

357 StaticStorage<Font>::Accessor storage(sFontCache);

358 storage.Retain();

359

360#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH

361 mFontCollection = sk_make_sp<skia::textlayout::FontCollection>();

362 mFontCollection->enableFontFallback();

363 mFontCollection->setDefaultFontManager(SkFontMgrRefDefault());

364#endif

365}

366

367IGraphicsSkia::~IGraphicsSkia()

368{

369 StaticStorage<Font>::Accessor storage(sFontCache);

370 storage.Release();

371}

372

374{

375 char extLower[32];

377 return (strstr(extLower, "png") != nullptr) || (strstr(extLower, "jpg") != nullptr) || (strstr(extLower, "jpeg") != nullptr);

378}

379

381{

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399#ifdef OS_WIN

400 if (location == EResourceLocation::kWinBinary)

401 {

402 int size = 0;

404 return new Bitmap(pData, size, scale);

405 }

406 else

407#endif

408 return new Bitmap(fileNameOrResID, scale);

409}

410

412{

413 return new Bitmap(pData, dataSize, scale);

414}

415

417{

418#if defined IGRAPHICS_GL

419#if defined OS_MAC

420 auto glInterface = GrGLInterfaces::MakeMac();

421#elif defined OS_WIN

422 auto glInterface = GrGLInterfaces::MakeWin();

423#endif

424 mGrContext = GrDirectContexts::MakeGL(glInterface);

425#elif defined IGRAPHICS_METAL

426 CAMetalLayer* pMTLLayer = (CAMetalLayer*) pContext;

427 id<MTLDevice> device = pMTLLayer.device;

428 id<MTLCommandQueue> commandQueue = [device newCommandQueue];

429 GrMtlBackendContext backendContext = {};

430 backendContext.fDevice.retain((__bridge GrMTLHandle) device);

431 backendContext.fQueue.retain((__bridge GrMTLHandle) commandQueue);

432 mGrContext = GrDirectContexts::MakeMetal(backendContext);

433 mMTLDevice = (void*) device;

434 mMTLCommandQueue = (void*) commandQueue;

435 mMTLLayer = pContext;

436#endif

437

439}

440

442{

444

445#if defined IGRAPHICS_GL

446 mSurface = nullptr;

447 mScreenSurface = nullptr;

448 mGrContext = nullptr;

449#elif defined IGRAPHICS_METAL

450 [(id<MTLCommandQueue>) mMTLCommandQueue release];

451 mMTLCommandQueue = nullptr;

452 mMTLLayer = nullptr;

453 mMTLDevice = nullptr;

454#endif

455}

456

458{

462

463#if defined IGRAPHICS_GL || defined IGRAPHICS_METAL

464 if (mGrContext.get())

465 {

466 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);

467 mSurface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);

468 }

469#else

470 #ifdef OS_WIN

471 mSurface.reset();

472

473 const size_t bmpSize = sizeof(BITMAPINFOHEADER) + (w * h * sizeof(uint32_t));

474 mSurfaceMemory.Resize(bmpSize);

475 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());

476 ZeroMemory(bmpInfo, sizeof(BITMAPINFO));

477 bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

478 bmpInfo->bmiHeader.biWidth = w;

479 bmpInfo->bmiHeader.biHeight = -h;

480 bmpInfo->bmiHeader.biPlanes = 1;

481 bmpInfo->bmiHeader.biBitCount = 32;

482 bmpInfo->bmiHeader.biCompression = BI_RGB;

483 void* pixels = bmpInfo->bmiColors;

484

485 SkImageInfo info = SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType, nullptr);

486 mSurface = SkSurfaces::WrapPixels(info, pixels, sizeof(uint32_t) * w);

487 #else

488 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);

489 mSurface = SkSurfaces::Raster(info);

490 #endif

491#endif

492 if (mSurface)

493 {

494 mCanvas = mSurface->getCanvas();

495 mCanvas->save();

496 }

497}

498

500{

501#if defined IGRAPHICS_GL

502 if (mGrContext.get())

503 {

506

507

508 int fbo = 0, samples = 0, stencilBits = 0;

509 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);

510 glGetIntegerv(GL_SAMPLES, &samples);

511#ifdef IGRAPHICS_GL3

512 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits);

513#else

514 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);

515#endif

516

517 GrGLFramebufferInfo fbInfo;

518 fbInfo.fFBOID = fbo;

519 fbInfo.fFormat = 0x8058;

520

521 auto backendRT = GrBackendRenderTargets::MakeGL(width, height, samples, stencilBits, fbInfo);

522

523 mScreenSurface = SkSurfaces::WrapBackendRenderTarget(mGrContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, nullptr);

524 assert(mScreenSurface);

525 }

526#elif defined IGRAPHICS_METAL

527 if (mGrContext.get())

528 {

531

532 id<CAMetalDrawable> drawable = [(CAMetalLayer*) mMTLLayer nextDrawable];

533

534 GrMtlTextureInfo fbInfo;

535 fbInfo.fTexture.retain((const void*)(drawable.texture));

536 auto backendRT = GrBackendRenderTargets::MakeMtl(width, height, fbInfo);

537 mScreenSurface = SkSurfaces::WrapBackendRenderTarget(mGrContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr);

538

539 mMTLDrawable = (void*) drawable;

540 assert(mScreenSurface);

541 }

542#endif

543

545}

546

548{

549#ifdef IGRAPHICS_CPU

550 #if defined OS_MAC || defined OS_IOS

551 SkPixmap pixmap;

552 mSurface->peekPixels(&pixmap);

553 SkBitmap bmp;

554 bmp.installPixels(pixmap);

556 CGContextSaveGState(pCGContext);

558 SkCGDrawBitmap(pCGContext, bmp, 0, 0);

559 CGContextRestoreGState(pCGContext);

560 #elif defined OS_WIN

563 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());

565 PAINTSTRUCT ps;

566 HDC hdc = BeginPaint(hWnd, &ps);

567 StretchDIBits(hdc, 0, 0, w, h, 0, 0, w, h, bmpInfo->bmiColors, bmpInfo, DIB_RGB_COLORS, SRCCOPY);

568 ReleaseDC(hWnd, hdc);

569 EndPaint(hWnd, &ps);

570 #else

571 #error NOT IMPLEMENTED

572 #endif

573#else

574 mSurface->draw(mScreenSurface->getCanvas(), 0.0, 0.0, nullptr);

575

576 if (auto dContext = GrAsDirectContext(mScreenSurface->getCanvas()->recordingContext())) {

577 dContext->flushAndSubmit();

578 }

579

580 #ifdef IGRAPHICS_METAL

581 id<MTLCommandBuffer> commandBuffer = [(id<MTLCommandQueue>) mMTLCommandQueue commandBuffer];

582 commandBuffer.label = @"Present";

583

584 [commandBuffer presentDrawable:(id<CAMetalDrawable>) mMTLDrawable];

585 [commandBuffer commit];

586 #endif

587#endif

588}

589

591{

592 SkPaint p;

593

594 p.setAntiAlias(true);

595 p.setBlendMode(SkiaBlendMode(pBlend));

596 if (pBlend)

597 p.setAlpha(Clip(static_cast<int>(pBlend->mWeight * 255), 0, 255));

598

600

603

604 mCanvas->save();

605 mCanvas->clipRect(SkiaRect(dest));

606 mCanvas->translate(dest.L, dest.T);

607 mCanvas->scale(scale1, scale1);

608 mCanvas->translate(-srcX * scale2, -srcY * scale2);

609

610#ifdef IGRAPHICS_CPU

611 auto samplingOptions = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);

612#else

613 auto samplingOptions = SkSamplingOptions(SkCubicResampler::Mitchell());

614#endif

615

616 if (image->mIsSurface)

617 image->mSurface->draw(mCanvas, 0.0, 0.0, samplingOptions, &p);

618 else

619 mCanvas->drawImage(image->mImage, 0.0, 0.0, samplingOptions, &p);

620

621 mCanvas->restore();

622}

623

625{

626 SkPath arc;

627 arc.setIsVolatile(true);

628

629 float sweep = (a2 - a1);

630

631 if (sweep >= 360.f || sweep <= -360.f)

632 {

633 arc.addCircle(cx, cy, r);

634 mMainPath.addPath(arc, mMatrix, SkPath::kAppend_AddPathMode);

635 }

636 else

637 {

638 if (winding == EWinding::CW)

639 {

640 while (sweep < 0)

641 sweep += 360.f;

642 }

643 else

644 {

645 while (sweep > 0)

646 sweep -= 360.f;

647 }

648

649 arc.arcTo(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, sweep, false);

650 mMainPath.addPath(arc, mMatrix, SkPath::kExtend_AddPathMode);

651 }

652}

653

655{

656 SkBitmap bitmap;

657 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));

658 mCanvas->readPixels(bitmap, x, y);

659 auto color = bitmap.getColor(0,0);

660 return IColor(SkColorGetA(color), SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));

661}

662

664{

665 StaticStorage<Font>::Accessor storage(sFontCache);

666 Font* cached = storage.Find(fontID);

667

668 if (cached)

669 return true;

670

671 IFontDataPtr data = font->GetFontData();

672

673 if (data->IsValid())

674 {

675 auto wrappedData = SkData::MakeWithoutCopy(data->Get(), data->GetSize());

676 auto typeFace = SkFontMgrRefDefault()->makeFromData(wrappedData);

677 if (typeFace)

678 {

679 storage.Add(new Font(std::move(data), typeFace), fontID);

680 return true;

681 }

682 }

683

684 return false;

685}

686

687void IGraphicsSkia::PrepareAndMeasureText(const IText& text, const char* str, IRECT& r, double& x, double & y, SkFont& font) const

688{

689 SkFontMetrics metrics;

690 SkPaint paint;

691

692

693 StaticStorage<Font>::Accessor storage(sFontCache);

694 Font* pFont = storage.Find(text.mFont);

695

696 assert(pFont && "No font found - did you forget to load it?");

697

698 font.setTypeface(pFont->mTypeface);

699 font.setHinting(SkFontHinting::kSlight);

700 font.setForceAutoHinting(false);

701 font.setSubpixel(true);

702 font.setSize(text.mSize * pFont->mData->GetHeightEMRatio());

703

704

705 const double textWidth = font.measureText(str, strlen(str), SkTextEncoding::kUTF8, nullptr);

706 font.getMetrics(&metrics);

707

708 const double textHeight = text.mSize;

709 const double ascender = metrics.fAscent;

710 const double descender = metrics.fDescent;

711

712 switch (text.mAlign)

713 {

714 case EAlign::Near: x = r.L; break;

715 case EAlign::Center: x = r.MW() - (textWidth / 2.0); break;

716 case EAlign::Far: x = r.R - textWidth; break;

717 }

718

719 switch (text.mVAlign)

720 {

721 case EVAlign::Top: y = r.T - ascender; break;

722 case EVAlign::Middle: y = r.MH() - descender + (textHeight / 2.0); break;

723 case EVAlign::Bottom: y = r.B - descender; break;

724 }

725

726 r = IRECT((float) x, (float) y + ascender, (float) (x + textWidth), (float) (y + ascender + textHeight));

727}

728

730{

731 SkFont font;

732 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);

733

734 IRECT r = bounds;

735 double x, y;

736 PrepareAndMeasureText(text, str, bounds, x, y, font);

738 return bounds.W();

739}

740

742{

743 IRECT measured = bounds;

744

745 SkFont font;

746 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);

747

748 double x, y;

749

750 PrepareAndMeasureText(text, str, measured, x, y, font);

752 DoTextRotation(text, bounds, measured);

753 SkPaint paint;

754 paint.setColor(SkiaColor(text.mFGColor, pBlend));

755 mCanvas->drawSimpleText(str, strlen(str), SkTextEncoding::kUTF8, x, y, font, paint);

757}

758

760{

761 SkPaint paint = SkiaPaint(pattern, pBlend);

762 paint.setStyle(SkPaint::kStroke_Style);

763

764 switch (options.mCapOption)

765 {

766 case ELineCap::Butt: paint.setStrokeCap(SkPaint::kButt_Cap); break;

767 case ELineCap::Round: paint.setStrokeCap(SkPaint::kRound_Cap); break;

768 case ELineCap::Square: paint.setStrokeCap(SkPaint::kSquare_Cap); break;

769 }

770

771 switch (options.mJoinOption)

772 {

773 case ELineJoin::Miter: paint.setStrokeJoin(SkPaint::kMiter_Join); break;

774 case ELineJoin::Round: paint.setStrokeJoin(SkPaint::kRound_Join); break;

775 case ELineJoin::Bevel: paint.setStrokeJoin(SkPaint::kBevel_Join); break;

776 }

777

778 if (options.mDash.GetCount())

779 {

780

781 int dashCount = options.mDash.GetCount();

782 int dashMax = dashCount & 1 ? dashCount * 2 : dashCount;

783 float dashArray[16];

784

785 for (int i = 0; i < dashMax; i += 2)

786 {

787 dashArray[i + 0] = options.mDash.GetArray()[i % dashCount];

788 dashArray[i + 1] = options.mDash.GetArray()[(i + 1) % dashCount];

789 }

790

791 paint.setPathEffect(SkDashPathEffect::Make(dashArray, dashMax, options.mDash.GetOffset()));

792 }

793

794 paint.setStrokeWidth(thickness);

795 paint.setStrokeMiter(options.mMiterLimit);

796

797 RenderPath(paint);

798

799 if (!options.mPreserve)

800 mMainPath.reset();

801}

802

804{

805 SkPaint paint = SkiaPaint(pattern, pBlend);

806 paint.setStyle(SkPaint::kFill_Style);

807

808 if (options.mFillRule == EFillRule::Winding)

809 mMainPath.setFillType(SkPathFillType::kWinding);

810 else

811 mMainPath.setFillType(SkPathFillType::kEvenOdd);

812

813 RenderPath(paint);

814

815 if (!options.mPreserve)

816 mMainPath.reset();

817}

818

819#ifdef IGRAPHICS_DRAWFILL_DIRECT

821{

822 auto paint = SkiaPaint(color, pBlend);

823 paint.setStyle(SkPaint::Style::kStroke_Style);

824 paint.setStrokeWidth(thickness);

825 mCanvas->drawRect(SkiaRect(bounds), paint);

826}

827

829{

830 auto paint = SkiaPaint(color, pBlend);

831 paint.setStyle(SkPaint::Style::kStroke_Style);

832 paint.setStrokeWidth(thickness);

833 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);

834}

835

836void IGraphicsSkia::DrawArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend, float thickness)

837{

838 auto paint = SkiaPaint(color, pBlend);

839 paint.setStyle(SkPaint::Style::kStroke_Style);

840 paint.setStrokeWidth(thickness);

841 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), false, paint);

842}

843

845{

846 auto paint = SkiaPaint(color, pBlend);

847 paint.setStyle(SkPaint::Style::kStroke_Style);

848 paint.setStrokeWidth(thickness);

849 mCanvas->drawCircle(cx, cy, r, paint);

850}

851

853{

854 auto paint = SkiaPaint(color, pBlend);

855 paint.setStyle(SkPaint::Style::kStroke_Style);

856 paint.setStrokeWidth(thickness);

857 mCanvas->drawOval(SkiaRect(bounds), paint);

858}

859

861{

862 auto paint = SkiaPaint(color, pBlend);

863 paint.setStyle(SkPaint::Style::kFill_Style);

864 mCanvas->drawRect(SkiaRect(bounds), paint);

865}

866

868{

869 auto paint = SkiaPaint(color, pBlend);

870 paint.setStyle(SkPaint::Style::kFill_Style);

871 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);

872}

873

875{

876 auto paint = SkiaPaint(color, pBlend);

877 paint.setStyle(SkPaint::Style::kFill_Style);

878 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), true, paint);

879}

880

882{

883 auto paint = SkiaPaint(color, pBlend);

884 paint.setStyle(SkPaint::Style::kFill_Style);

885 mCanvas->drawCircle(cx, cy, r, paint);

886}

887

889{

890 auto paint = SkiaPaint(color, pBlend);

891 paint.setStyle(SkPaint::Style::kFill_Style);

892 mCanvas->drawOval(SkiaRect(bounds), paint);

893}

894#endif

895

896void IGraphicsSkia::RenderPath(SkPaint& paint)

897{

898 SkMatrix invMatrix;

899

900 if (!mMatrix.isIdentity() && mMatrix.invert(&invMatrix))

901 {

902 SkPath path;

903 path.setIsVolatile(true);

904 mMainPath.transform(invMatrix, &path);

905 mCanvas->drawPath(path, paint);

906 }

907 else

908 {

909 mCanvas->drawPath(mMainPath, paint);

910 }

911}

912

913void IGraphicsSkia::PathTransformSetMatrix(const IMatrix& m)

914{

915 double xTranslate = 0.0;

916 double yTranslate = 0.0;

917

918 if (!mCanvas)

919 return;

920

921 if (!mLayers.empty())

922 {

923 IRECT bounds = mLayers.top()->Bounds();

924

925 xTranslate = -bounds.L;

926 yTranslate = -bounds.T;

927 }

928

929 mMatrix = SkMatrix::MakeAll(m.mXX, m.mXY, m.mTX, m.mYX, m.mYY, m.mTY, 0, 0, 1);

931 SkMatrix globalMatrix = SkMatrix::Scale(scale, scale);

932 mClipMatrix = SkMatrix();

933 mFinalMatrix = mMatrix;

934 globalMatrix.preTranslate(xTranslate, yTranslate);

935 mClipMatrix.postConcat(globalMatrix);

936 mFinalMatrix.postConcat(globalMatrix);

937 mCanvas->setMatrix(mFinalMatrix);

938}

939

940void IGraphicsSkia::SetClipRegion(const IRECT& r)

941{

942 mCanvas->restoreToCount(0);

943 mCanvas->save();

944 mCanvas->setMatrix(mClipMatrix);

945 mCanvas->clipRect(SkiaRect(r));

946 mCanvas->setMatrix(mFinalMatrix);

947}

948

950{

951 sk_sp<SkSurface> surface;

952 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);

953

954 #ifndef IGRAPHICS_CPU

955 if (cacheable)

956 {

957 surface = SkSurfaces::Raster(info);

958 }

959 else

960 {

961 surface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);

962 }

963 #else

964 surface = SkSurfaces::Raster(info);

965 #endif

966

967 surface->getCanvas()->save();

968

969 return new Bitmap(std::move(surface), width, height, scale, drawScale);

970}

971

973{

974 mCanvas = mLayers.empty() ? mSurface->getCanvas() : mLayers.top()->GetAPIBitmap()->GetBitmap()->mSurface->getCanvas();

975}

976

977static size_t CalcRowBytes(int width)

978{

979 width = ((width + 7) & (-8));

980 return width * sizeof(uint32_t);

981}

982

984{

985#ifdef IGRAPHICS_CPU

986 bool useFilter = false;

987#else

988 bool useFilter = shadow.mPattern.mNStops <= 1;

989#endif

990

991 if (useFilter)

992 {

993 auto makeFilter = [&shadow](float scale)

994 {

995

996 const auto dx = shadow.mXOffset * scale;

997 const auto dy = shadow.mYOffset * scale;

998 const auto r = shadow.mBlurSize * scale / 3.f;

999 const IBlend blend(EBlend::Default, shadow.mOpacity);

1000 const auto color = SkiaColor(shadow.mPattern.GetStop(0).mColor, &blend);

1001

1002 if (shadow.mDrawForeground)

1003 return SkImageFilters::DropShadow(dx, dy, r, r, color, nullptr);

1004 else

1005 return SkImageFilters::DropShadowOnly(dx, dy, r, r, color, nullptr);

1006 };

1007

1008 auto shadowFilter = makeFilter(layer->GetAPIBitmap()->GetScale());

1009

1010 SkPaint p;

1011 SkMatrix m;

1012 m.reset();

1013

1014 p.setAntiAlias(true);

1015 p.setImageFilter(shadowFilter);

1016

1017 sk_sp<SkSurface> surface = layer->GetAPIBitmap()->GetBitmap()->mSurface;

1018 SkCanvas* pCanvas = surface->getCanvas();

1019 sk_sp<SkImage> contents = surface->makeImageSnapshot();

1020

1021 pCanvas->setMatrix(m);

1022 pCanvas->clear(SK_ColorTRANSPARENT);

1023 pCanvas->drawImage(contents.get(), 0.0, 0.0, SkSamplingOptions(), &p);

1024 }

1025 else

1026 {

1028 }

1029}

1030

1032{

1033 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();

1034 size_t rowBytes = CalcRowBytes(pDrawable->mSurface->width());

1035 int size = pDrawable->mSurface->height() * static_cast<int>(rowBytes);

1036

1037 data.Resize(size);

1038

1039 if (data.GetSize() >= size)

1040 {

1041 SkImageInfo info = SkImageInfo::MakeN32Premul(pDrawable->mSurface->width(), pDrawable->mSurface->height());

1042 pDrawable->mSurface->readPixels(info, data.Get(), rowBytes, 0, 0);

1043 }

1044}

1045

1047{

1048 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();

1049 int width = pDrawable->mSurface->width();

1050 int height = pDrawable->mSurface->height();

1051 size_t rowBytes = CalcRowBytes(width);

1052 double scale = layer->GetAPIBitmap()->GetDrawScale() * layer->GetAPIBitmap()->GetScale();

1053

1054 SkCanvas* pCanvas = pDrawable->mSurface->getCanvas();

1055

1056 SkMatrix m;

1057 m.reset();

1058

1059 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);

1060 SkPixmap pixMap(info, mask.Get(), rowBytes);

1061 sk_sp<SkImage> image = SkImages::RasterFromPixmap(pixMap, nullptr, nullptr);

1062 sk_sp<SkImage> foreground;

1063

1064

1065

1066 if (shadow.mDrawForeground)

1067 foreground = pDrawable->mSurface->makeImageSnapshot();

1068

1069 pCanvas->clear(SK_ColorTRANSPARENT);

1070

1071 IBlend blend(EBlend::Default, shadow.mOpacity);

1072 pCanvas->setMatrix(m);

1073 pCanvas->drawImage(image.get(), shadow.mXOffset * scale, shadow.mYOffset * scale);

1074 m = SkMatrix::Scale(scale, scale);

1075 pCanvas->setMatrix(m);

1076 pCanvas->translate(-layer->Bounds().L, -layer->Bounds().T);

1077 SkPaint p = SkiaPaint(shadow.mPattern, &blend);

1078 p.setBlendMode(SkBlendMode::kSrcIn);

1079 pCanvas->drawPaint(p);

1080

1081 if (shadow.mDrawForeground)

1082 {

1083 m.reset();

1084 pCanvas->setMatrix(m);

1085 pCanvas->drawImage(foreground.get(), 0.0, 0.0);

1086 }

1087}

1088

1090{

1091 SkRect r = SkiaRect(innerBounds.GetTranslated(xyDrop, xyDrop));

1092

1093 SkPaint paint = SkiaPaint(COLOR_BLACK_DROP_SHADOW, pBlend);

1094 paint.setStyle(SkPaint::Style::kFill_Style);

1095

1096 paint.setMaskFilter(SkMaskFilter::MakeBlur(kSolid_SkBlurStyle, blur * 0.5));

1097 mCanvas->drawRoundRect(r, roundness, roundness, paint);

1098}

1099

1101{

1102#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH

1103 using namespace skia::textlayout;

1104

1105 auto ConvertTextAlign = [](EAlign align) {

1106 switch (align)

1107 {

1108 case EAlign::Near: return TextAlign::kLeft;

1109 case EAlign::Center: return TextAlign::kCenter;

1110 case EAlign::Far: return TextAlign::kRight;

1111 default: return TextAlign::kLeft;

1112 }

1113 };

1114

1115 ParagraphStyle paragraphStyle;

1116 paragraphStyle.setTextAlign(ConvertTextAlign(text.mAlign));

1117

1118 auto builder = ParagraphBuilder::make(paragraphStyle, mFontCollection, GetUnicode());

1119

1120 assert(builder && "Paragraph Builder couldn't be created");

1121

1122 TextStyle textStyle;

1123 textStyle.setColor(SkiaColor(text.mFGColor, pBlend));

1124

1125 std::string fontFamily = text.mFont;

1126

1127 size_t pos = fontFamily.find('-');

1128 if (pos != std::string::npos)

1129 {

1130 fontFamily = fontFamily.substr(0, pos);

1131 }

1132

1133 textStyle.setFontFamilies({SkString(fontFamily)});

1134 textStyle.setFontSize(text.mSize * 0.9);

1135 textStyle.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight,

1136 SkFontStyle::kNormal_Width,

1137 SkFontStyle::kUpright_Slant));

1138

1139 builder->pushStyle(textStyle);

1140 builder->addText(str);

1141 builder->pop();

1142

1143 auto paragraph = builder->Build();

1144

1145 paragraph->layout(bounds.W());

1146

1147 float yOffset = 0;

1148 switch (text.mVAlign)

1149 {

1150 case EVAlign::Top:

1151 yOffset = 0;

1152 break;

1153 case EVAlign::Middle:

1154 yOffset = (bounds.H() - paragraph->getHeight()) / 2;

1155 break;

1156 case EVAlign::Bottom:

1157 yOffset = bounds.H() - paragraph->getHeight();

1158 break;

1159 }

1160

1161 mCanvas->save();

1162 mCanvas->translate(bounds.L, bounds.T + yOffset);

1163 paragraph->paint(mCanvas, 0, 0);

1164 mCanvas->restore();

1165#else

1166 DrawText(text, str, bounds, pBlend);

1167#endif

1168}

1169

1171{

1172#ifdef IGRAPHICS_CPU

1173 return "SKIA | CPU";

1174#elif defined IGRAPHICS_GL2

1175 return "SKIA | GL2";

1176#elif defined IGRAPHICS_GL3

1177 return "SKIA | GL3";

1178#elif defined IGRAPHICS_METAL

1179 return "SKIA | Metal";

1180#endif

1181}

const void * LoadWinResource(const char *resID, const char *type, int &sizeInBytes, void *pHInstance)

Load a resource from the binary (windows only).

A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...

BitmapData GetBitmap() const

void SetBitmap(BitmapData pBitmap, int w, int h, float scale, float drawScale)

Used to initialise the members after construction.

User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...

float GetDrawScale() const

APIBitmap * GetAPIBitmap() const

An editor delegate base class that uses IGraphics for the UI.

The lowest level base class of an IGraphics context.

virtual void DrawRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0, float thickness=1.f)

Draw a rectangle to the graphics context.

virtual void FillEllipse(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0)

Fill an ellipse within a rectangular region of the graphics context.

virtual void DrawEllipse(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0, float thickness=1.f)

Draw an ellipse within a rectangular region of the graphics context.

void DrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=0)

Draw some text to the graphics context in a specific rectangle.

float GetTotalScale() const

Gets the combined draw and screen/display scaling factor.

virtual void * GetWindow()=0

Get a pointer to the platform view e.g.

virtual void DrawRoundRect(const IColor &color, const IRECT &bounds, float cornerRadius=5.f, const IBlend *pBlend=0, float thickness=1.f)

Draw a rounded rectangle to the graphics context.

void DoMeasureTextRotation(const IText &text, const IRECT &bounds, IRECT &rect) const

Measures text bounds accounting for rotation.

int WindowWidth() const

Gets the width of the graphics context including draw scaling.

void PathTransformRestore()

Restore the affine transform of the current path, to the previously saved state.

virtual void FillRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0)

Fill a rectangular region of the graphics context with a color.

void * GetPlatformContext()

Get the platform level draw context - an HDC or CGContextRef.

virtual void DrawArc(const IColor &color, float cx, float cy, float r, float a1, float a2, const IBlend *pBlend=0, float thickness=1.f)

Draw an arc to the graphics context.

void RemoveAllControls()

Removes all regular IControls from the control list, as well as special controls (frees memory).

virtual void DrawCircle(const IColor &color, float cx, float cy, float r, const IBlend *pBlend=0, float thickness=1.f)

Draw a circle to the graphics context.

virtual void FillRoundRect(const IColor &color, const IRECT &bounds, float cornerRadius=5.f, const IBlend *pBlend=0)

Fill a rounded rectangle with a color.

virtual void * GetWinModuleHandle()

virtual void ApplyLayerDropShadow(ILayerPtr &layer, const IShadow &shadow)

Applies a drop shadow directly onto a layer.

float GetScreenScale() const

Gets the screen/display scaling factor, e.g.

void PathTransformSave()

Save the current affine transform of the current path.

virtual void FillArc(const IColor &color, float cx, float cy, float r, float a1, float a2, const IBlend *pBlend=0)

Fill an arc segment with a color.

int WindowHeight() const

Gets the height of the graphics context including draw scaling.

virtual void BeginFrame()

Called at the beginning of drawing.

virtual void FillCircle(const IColor &color, float cx, float cy, float r, const IBlend *pBlend=0)

Fill a circle with a color.

APIBitmap * LoadAPIBitmap(const char *fileNameOrResID, int scale, EResourceLocation location, const char *ext) override

Drawing API method to load a bitmap, called internally.

void BeginFrame() override

Called at the beginning of drawing.

void GetLayerBitmapData(const ILayerPtr &layer, RawBitmapData &data) override

Get the contents of a layer as Raw RGBA bitmap data NOTE: you should only call this within IControl::...

void DrawResize() override

Called to update the drawing surface after a resize.

void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override

Internal method to draw text.

void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options, const IBlend *pBlend) override

Stroke the current current path.

void EndFrame() override

Called by some drawing API classes to finally blit the draw bitmap onto the screen or perform other c...

float DoMeasureText(const IText &text, const char *str, IRECT &bounds) const override

Internal method to measure text dimensions.

void PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding) override

Add an arc to the current path.

void ApplyShadowMask(ILayerPtr &layer, RawBitmapData &mask, const IShadow &shadow) override

Implemented by a graphics backend to apply a calculated shadow mask to a layer, according to the shad...

const char * GetDrawingAPIStr() override

APIBitmap * CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable=false) override

Creates a new API bitmap, either in memory or as a GPU texture.

bool LoadAPIFont(const char *fontID, const PlatformFontPtr &font) override

Drawing API method to load a font from a PlatformFontPtr, called internally.

void OnViewInitialized(void *pContext) override

Called after platform view initialization, so that drawing classes can e.g.

void UpdateLayer() override

Implemented by a graphics backend to prepare for drawing to the layer at the top of the stack.

void DrawMultiLineText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override

Draw some multi-line text to the graphics context in a specific rectangle (NanoVG only)

void PathFill(const IPattern &pattern, const IFillOptions &options, const IBlend *pBlend) override

Fill the current current path.

bool BitmapExtSupported(const char *ext) override

Checks a file extension and reports whether this drawing API supports loading that extension.

IColor GetPoint(int x, int y) override

Get the color at an X, Y location in the graphics context.

void ApplyLayerDropShadow(ILayerPtr &layer, const IShadow &shadow) override

Applies a drop shadow directly onto a layer.

void DrawBitmap(const IBitmap &bitmap, const IRECT &dest, int srcX, int srcY, const IBlend *pBlend) override

Draw a bitmap (raster) image to the graphics context.

void DrawFastDropShadow(const IRECT &innerBounds, const IRECT &outerBounds, float xyDrop, float roundness, float blur, IBlend *pBlend) override

NanoVG only.

void OnViewDestroyed() override

Called after a platform view is destroyed, so that drawing classes can e.g.

std::unique_ptr< ILayer > ILayerPtr

ILayerPtr is a managed pointer for transferring the ownership of layers.

Used to manage stroke behaviour for path based drawing back ends.

BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)

Clips the value x between lo and hi.

static void ToLower(char *cDest, const char *cSrc)

Converts a C string to lowercase.

Used to manage composite/blend operations, independent of draw class/platform.

Used to manage color data, independent of draw class/platform.

Used to represent a point/stop in a gradient.

Used to manage fill behaviour.

Used to store transformation matrices.

IMatrix & Invert()

Changes the matrix to be the inverse of its original value.

void TransformPoint(double &x, double &y, double x0, double y0) const

Transforms a point using this matrix.

Used to store pattern information for gradients.

const IColorStop & GetStop(int idx) const

Get the IColorStop at a particular index (will crash if out of bounds)

Used to manage a rectangular area, independent of draw class/platform.

IRECT GetTranslated(float x, float y) const

Get a translated copy of this rectangle.

Used to specify properties of a drop-shadow to a layer.

IText is used to manage font and text/text entry style for a piece of text on the UI,...