4#include "IGraphicsSkia.h"
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"
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"
32#include "include/gpu/ganesh/SkSurfaceGanesh.h"
33#include "include/gpu/GrBackendSurface.h"
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
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
52 #include "include/ports/SkFontMgr_mac_ct.h"
55 #include "include/ports/SkTypeface_win.h"
57 #pragma comment(lib, "skia.lib")
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")
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"
75 #include "include/gpu/ganesh/gl/mac/GrGLMakeMacInterface.h"
77 #include "include/gpu/ganesh/gl/win/GrGLMakeWinInterface.h"
78 #pragma comment(lib, "opengl32.lib")
84using namespace igraphics;
86extern std::map<std::string, MTLTexturePtr> gTextureMap;
88#pragma mark - Private Classes and Structs
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);
99 SkiaDrawable mDrawable;
102IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkSurface> surface,
int width,
int height,
float scale,
float drawScale)
104 mDrawable.mSurface = surface;
105 mDrawable.mIsSurface =
true;
107 SetBitmap(&mDrawable, width, height, scale, drawScale);
110IGraphicsSkia::Bitmap::Bitmap(
const char* path,
double sourceScale)
112 sk_sp<SkData> data = SkData::MakeFromFileName(path);
114 assert(data &&
"Unable to load file at path");
116 auto image = SkImages::DeferredFromEncodedData(data);
119 image = image->makeRasterImage();
122 mDrawable.mImage = image;
124 mDrawable.mIsSurface =
false;
125 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
128IGraphicsSkia::Bitmap::Bitmap(
const void* pData,
int size,
double sourceScale)
130 auto data = SkData::MakeWithoutCopy(pData, size);
131 auto image = SkImages::DeferredFromEncodedData(data);
134 image = image->makeRasterImage();
137 mDrawable.mImage = image;
139 mDrawable.mIsSurface =
false;
140 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
143IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkImage> image,
double sourceScale)
146 mDrawable.mImage = image->makeRasterImage();
148 mDrawable.mImage = image;
151 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
154struct IGraphicsSkia::Font
156 Font(IFontDataPtr&& data, sk_sp<SkTypeface> typeFace)
157 : mData(std::move(data)), mTypeface(typeFace) {}
160 sk_sp<SkTypeface> mTypeface;
164StaticStorage<IGraphicsSkia::Font> IGraphicsSkia::sFontCache;
166#pragma mark - Utility conversions
169BEGIN_IGRAPHICS_NAMESPACE
171SkColor SkiaColor(
const IColor& color,
const IBlend* pBlend)
174 return SkColorSetARGB(
Clip(
static_cast<int>(pBlend->mWeight * color.A), 0, 255), color.R, color.G, color.B);
176 return SkColorSetARGB(color.A, color.R, color.G, color.B);
179SkRect SkiaRect(
const IRECT& r)
181 return SkRect::MakeLTRB(r.L, r.T, r.R, r.B);
184SkBlendMode SkiaBlendMode(
const IBlend* pBlend)
187 return SkBlendMode::kSrcOver;
189 switch (pBlend->mMethod)
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;
203 return SkBlendMode::kClear;
206SkTileMode SkiaTileMode(
const IPattern& pattern)
208 switch (pattern.mExtend)
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;
216 return SkTileMode::kClamp;
222 paint.setAntiAlias(
true);
223 paint.setBlendMode(SkiaBlendMode(pBlend));
224 int numStops = pattern.
NStops();
226 if (pattern.mType == EPatternType::Solid || numStops < 2)
228 paint.setColor(SkiaColor(pattern.
GetStop(0).mColor, pBlend));
237 IMatrix m = pattern.mTransform;
244 SkPoint::Make(x1, y1),
245 SkPoint::Make(x2, y2)
249 SkScalar positions[8];
251 assert(numStops <= 8);
253 for(
int i = 0; i < numStops; i++)
256 colors[i] = SkiaColor(stop.mColor, pBlend);
257 positions[i] = stop.mOffset;
260 switch (pattern.mType)
262 case EPatternType::Linear:
263 paint.setShader(SkGradientShader::MakeLinear(points, colors, positions, numStops, SkiaTileMode(pattern), 0,
nullptr));
266 case EPatternType::Radial:
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));
275 case EPatternType::Sweep:
277 SkMatrix matrix = SkMatrix::MakeAll(m.mXX, m.mYX, 0, m.mXY, m.mYY, 0, 0, 0, 1);
279 paint.setShader(SkGradientShader::MakeSweep(x1, y1, colors,
nullptr, numStops, SkTileMode::kDecal,
280 0, 360 * positions[numStops - 1], 0, &matrix));
293END_IGRAPHICS_NAMESPACE
298static sk_sp<SkFontMgr> SFontMgrFactory()
300#if defined OS_MAC || defined OS_IOS
301 return SkFontMgr_New_CoreText(
nullptr);
303 return SkFontMgr_New_DirectWrite();
305 #error "Not supported"
309bool gFontMgrFactoryCreated =
false;
311sk_sp<SkFontMgr> SkFontMgrRefDefault()
313 static std::once_flag flag;
314 static sk_sp<SkFontMgr> mgr;
315 std::call_once(flag, [] {
316 mgr = SFontMgrFactory();
317 gFontMgrFactoryCreated =
true;
322#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
324bool gSkUnicodeCreated =
false;
326sk_sp<SkUnicode> GetUnicode()
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;
337 DBGMSG(
"Could not load unicode data\n");
345IGraphicsSkia::IGraphicsSkia(
IGEditorDelegate& dlg,
int w,
int h,
int fps,
float scale)
348 mMainPath.setIsVolatile(
true);
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);
357 StaticStorage<Font>::Accessor storage(sFontCache);
360#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
361 mFontCollection = sk_make_sp<skia::textlayout::FontCollection>();
362 mFontCollection->enableFontFallback();
363 mFontCollection->setDefaultFontManager(SkFontMgrRefDefault());
367IGraphicsSkia::~IGraphicsSkia()
369 StaticStorage<Font>::Accessor storage(sFontCache);
377 return (strstr(extLower,
"png") !=
nullptr) || (strstr(extLower,
"jpg") !=
nullptr) || (strstr(extLower,
"jpeg") !=
nullptr);
400 if (location == EResourceLocation::kWinBinary)
404 return new Bitmap(pData, size, scale);
408 return new Bitmap(fileNameOrResID, scale);
413 return new Bitmap(pData, dataSize, scale);
418#if defined IGRAPHICS_GL
420 auto glInterface = GrGLInterfaces::MakeMac();
422 auto glInterface = GrGLInterfaces::MakeWin();
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;
445#if defined IGRAPHICS_GL
447 mScreenSurface =
nullptr;
448 mGrContext =
nullptr;
449#elif defined IGRAPHICS_METAL
450 [(id<MTLCommandQueue>) mMTLCommandQueue release];
451 mMTLCommandQueue =
nullptr;
453 mMTLDevice =
nullptr;
463#if defined IGRAPHICS_GL || defined IGRAPHICS_METAL
464 if (mGrContext.get())
466 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
467 mSurface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);
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;
485 SkImageInfo info = SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType,
nullptr);
486 mSurface = SkSurfaces::WrapPixels(info, pixels,
sizeof(uint32_t) * w);
488 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
489 mSurface = SkSurfaces::Raster(info);
494 mCanvas = mSurface->getCanvas();
501#if defined IGRAPHICS_GL
502 if (mGrContext.get())
508 int fbo = 0, samples = 0, stencilBits = 0;
509 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
510 glGetIntegerv(GL_SAMPLES, &samples);
512 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits);
514 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
517 GrGLFramebufferInfo fbInfo;
519 fbInfo.fFormat = 0x8058;
521 auto backendRT = GrBackendRenderTargets::MakeGL(width, height, samples, stencilBits, fbInfo);
523 mScreenSurface = SkSurfaces::WrapBackendRenderTarget(mGrContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
nullptr,
nullptr);
524 assert(mScreenSurface);
526#elif defined IGRAPHICS_METAL
527 if (mGrContext.get())
532 id<CAMetalDrawable> drawable = [(CAMetalLayer*) mMTLLayer nextDrawable];
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);
539 mMTLDrawable = (
void*) drawable;
540 assert(mScreenSurface);
550 #if defined OS_MAC || defined OS_IOS
552 mSurface->peekPixels(&pixmap);
554 bmp.installPixels(pixmap);
556 CGContextSaveGState(pCGContext);
558 SkCGDrawBitmap(pCGContext, bmp, 0, 0);
559 CGContextRestoreGState(pCGContext);
563 BITMAPINFO* bmpInfo =
reinterpret_cast<BITMAPINFO*
>(mSurfaceMemory.Get());
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);
571 #error NOT IMPLEMENTED
574 mSurface->draw(mScreenSurface->getCanvas(), 0.0, 0.0,
nullptr);
576 if (
auto dContext = GrAsDirectContext(mScreenSurface->getCanvas()->recordingContext())) {
577 dContext->flushAndSubmit();
580 #ifdef IGRAPHICS_METAL
581 id<MTLCommandBuffer> commandBuffer = [(id<MTLCommandQueue>) mMTLCommandQueue commandBuffer];
582 commandBuffer.label =
@"Present";
584 [commandBuffer presentDrawable:(id<CAMetalDrawable>) mMTLDrawable];
585 [commandBuffer commit];
594 p.setAntiAlias(
true);
595 p.setBlendMode(SkiaBlendMode(pBlend));
597 p.setAlpha(
Clip(
static_cast<int>(pBlend->mWeight * 255), 0, 255));
605 mCanvas->clipRect(SkiaRect(dest));
606 mCanvas->translate(dest.L, dest.T);
607 mCanvas->scale(scale1, scale1);
608 mCanvas->translate(-srcX * scale2, -srcY * scale2);
611 auto samplingOptions = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
613 auto samplingOptions = SkSamplingOptions(SkCubicResampler::Mitchell());
616 if (image->mIsSurface)
617 image->mSurface->draw(mCanvas, 0.0, 0.0, samplingOptions, &p);
619 mCanvas->drawImage(image->mImage, 0.0, 0.0, samplingOptions, &p);
627 arc.setIsVolatile(
true);
629 float sweep = (a2 - a1);
631 if (sweep >= 360.f || sweep <= -360.f)
633 arc.addCircle(cx, cy, r);
634 mMainPath.addPath(arc, mMatrix, SkPath::kAppend_AddPathMode);
638 if (winding == EWinding::CW)
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);
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));
665 StaticStorage<Font>::Accessor storage(sFontCache);
666 Font* cached = storage.Find(fontID);
671 IFontDataPtr data = font->GetFontData();
675 auto wrappedData = SkData::MakeWithoutCopy(data->Get(), data->GetSize());
676 auto typeFace = SkFontMgrRefDefault()->makeFromData(wrappedData);
679 storage.Add(
new Font(std::move(data), typeFace), fontID);
687void IGraphicsSkia::PrepareAndMeasureText(
const IText& text,
const char* str,
IRECT& r,
double& x,
double & y, SkFont& font)
const
689 SkFontMetrics metrics;
693 StaticStorage<Font>::Accessor storage(sFontCache);
694 Font* pFont = storage.Find(text.mFont);
696 assert(pFont &&
"No font found - did you forget to load it?");
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());
705 const double textWidth = font.measureText(str, strlen(str), SkTextEncoding::kUTF8,
nullptr);
706 font.getMetrics(&metrics);
708 const double textHeight = text.mSize;
709 const double ascender = metrics.fAscent;
710 const double descender = metrics.fDescent;
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;
719 switch (text.mVAlign)
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;
726 r =
IRECT((
float) x, (
float) y + ascender, (
float) (x + textWidth), (
float) (y + ascender + textHeight));
732 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
736 PrepareAndMeasureText(text, str, bounds, x, y, font);
743 IRECT measured = bounds;
746 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
750 PrepareAndMeasureText(text, str, measured, x, y, font);
752 DoTextRotation(text, bounds, measured);
754 paint.setColor(SkiaColor(text.mFGColor, pBlend));
755 mCanvas->drawSimpleText(str, strlen(str), SkTextEncoding::kUTF8, x, y, font, paint);
761 SkPaint paint = SkiaPaint(pattern, pBlend);
762 paint.setStyle(SkPaint::kStroke_Style);
764 switch (options.mCapOption)
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;
771 switch (options.mJoinOption)
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;
778 if (options.mDash.GetCount())
781 int dashCount = options.mDash.GetCount();
782 int dashMax = dashCount & 1 ? dashCount * 2 : dashCount;
785 for (
int i = 0; i < dashMax; i += 2)
787 dashArray[i + 0] = options.mDash.GetArray()[i % dashCount];
788 dashArray[i + 1] = options.mDash.GetArray()[(i + 1) % dashCount];
791 paint.setPathEffect(SkDashPathEffect::Make(dashArray, dashMax, options.mDash.GetOffset()));
794 paint.setStrokeWidth(thickness);
795 paint.setStrokeMiter(options.mMiterLimit);
799 if (!options.mPreserve)
805 SkPaint paint = SkiaPaint(pattern, pBlend);
806 paint.setStyle(SkPaint::kFill_Style);
808 if (options.mFillRule == EFillRule::Winding)
809 mMainPath.setFillType(SkPathFillType::kWinding);
811 mMainPath.setFillType(SkPathFillType::kEvenOdd);
815 if (!options.mPreserve)
819#ifdef IGRAPHICS_DRAWFILL_DIRECT
822 auto paint = SkiaPaint(color, pBlend);
823 paint.setStyle(SkPaint::Style::kStroke_Style);
824 paint.setStrokeWidth(thickness);
825 mCanvas->drawRect(SkiaRect(bounds), paint);
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);
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);
846 auto paint = SkiaPaint(color, pBlend);
847 paint.setStyle(SkPaint::Style::kStroke_Style);
848 paint.setStrokeWidth(thickness);
849 mCanvas->drawCircle(cx, cy, r, paint);
854 auto paint = SkiaPaint(color, pBlend);
855 paint.setStyle(SkPaint::Style::kStroke_Style);
856 paint.setStrokeWidth(thickness);
857 mCanvas->drawOval(SkiaRect(bounds), paint);
862 auto paint = SkiaPaint(color, pBlend);
863 paint.setStyle(SkPaint::Style::kFill_Style);
864 mCanvas->drawRect(SkiaRect(bounds), paint);
869 auto paint = SkiaPaint(color, pBlend);
870 paint.setStyle(SkPaint::Style::kFill_Style);
871 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);
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);
883 auto paint = SkiaPaint(color, pBlend);
884 paint.setStyle(SkPaint::Style::kFill_Style);
885 mCanvas->drawCircle(cx, cy, r, paint);
890 auto paint = SkiaPaint(color, pBlend);
891 paint.setStyle(SkPaint::Style::kFill_Style);
892 mCanvas->drawOval(SkiaRect(bounds), paint);
896void IGraphicsSkia::RenderPath(SkPaint& paint)
900 if (!mMatrix.isIdentity() && mMatrix.invert(&invMatrix))
903 path.setIsVolatile(
true);
904 mMainPath.transform(invMatrix, &path);
905 mCanvas->drawPath(path, paint);
909 mCanvas->drawPath(mMainPath, paint);
913void IGraphicsSkia::PathTransformSetMatrix(
const IMatrix& m)
915 double xTranslate = 0.0;
916 double yTranslate = 0.0;
921 if (!mLayers.empty())
923 IRECT bounds = mLayers.top()->Bounds();
925 xTranslate = -bounds.L;
926 yTranslate = -bounds.T;
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);
940void IGraphicsSkia::SetClipRegion(
const IRECT& r)
942 mCanvas->restoreToCount(0);
944 mCanvas->setMatrix(mClipMatrix);
945 mCanvas->clipRect(SkiaRect(r));
946 mCanvas->setMatrix(mFinalMatrix);
951 sk_sp<SkSurface> surface;
952 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
954 #ifndef IGRAPHICS_CPU
957 surface = SkSurfaces::Raster(info);
961 surface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);
964 surface = SkSurfaces::Raster(info);
967 surface->getCanvas()->save();
969 return new Bitmap(std::move(surface), width, height, scale, drawScale);
974 mCanvas = mLayers.empty() ? mSurface->getCanvas() : mLayers.top()->GetAPIBitmap()->GetBitmap()->mSurface->getCanvas();
977static size_t CalcRowBytes(
int width)
979 width = ((width + 7) & (-8));
980 return width *
sizeof(uint32_t);
986 bool useFilter =
false;
988 bool useFilter = shadow.mPattern.mNStops <= 1;
993 auto makeFilter = [&shadow](
float scale)
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);
1002 if (shadow.mDrawForeground)
1003 return SkImageFilters::DropShadow(dx, dy, r, r, color,
nullptr);
1005 return SkImageFilters::DropShadowOnly(dx, dy, r, r, color,
nullptr);
1008 auto shadowFilter = makeFilter(layer->GetAPIBitmap()->GetScale());
1014 p.setAntiAlias(
true);
1015 p.setImageFilter(shadowFilter);
1017 sk_sp<SkSurface> surface = layer->GetAPIBitmap()->GetBitmap()->mSurface;
1018 SkCanvas* pCanvas = surface->getCanvas();
1019 sk_sp<SkImage> contents = surface->makeImageSnapshot();
1021 pCanvas->setMatrix(m);
1022 pCanvas->clear(SK_ColorTRANSPARENT);
1023 pCanvas->drawImage(contents.get(), 0.0, 0.0, SkSamplingOptions(), &p);
1033 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();
1034 size_t rowBytes = CalcRowBytes(pDrawable->mSurface->width());
1035 int size = pDrawable->mSurface->height() *
static_cast<int>(rowBytes);
1039 if (data.GetSize() >= size)
1041 SkImageInfo info = SkImageInfo::MakeN32Premul(pDrawable->mSurface->width(), pDrawable->mSurface->height());
1042 pDrawable->mSurface->readPixels(info, data.Get(), rowBytes, 0, 0);
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();
1054 SkCanvas* pCanvas = pDrawable->mSurface->getCanvas();
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;
1066 if (shadow.mDrawForeground)
1067 foreground = pDrawable->mSurface->makeImageSnapshot();
1069 pCanvas->clear(SK_ColorTRANSPARENT);
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);
1081 if (shadow.mDrawForeground)
1084 pCanvas->setMatrix(m);
1085 pCanvas->drawImage(foreground.get(), 0.0, 0.0);
1091 SkRect r = SkiaRect(innerBounds.
GetTranslated(xyDrop, xyDrop));
1093 SkPaint paint = SkiaPaint(COLOR_BLACK_DROP_SHADOW, pBlend);
1094 paint.setStyle(SkPaint::Style::kFill_Style);
1096 paint.setMaskFilter(SkMaskFilter::MakeBlur(kSolid_SkBlurStyle, blur * 0.5));
1097 mCanvas->drawRoundRect(r, roundness, roundness, paint);
1102#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
1103 using namespace skia::textlayout;
1105 auto ConvertTextAlign = [](EAlign align) {
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;
1115 ParagraphStyle paragraphStyle;
1116 paragraphStyle.setTextAlign(ConvertTextAlign(text.mAlign));
1118 auto builder = ParagraphBuilder::make(paragraphStyle, mFontCollection, GetUnicode());
1120 assert(builder &&
"Paragraph Builder couldn't be created");
1122 TextStyle textStyle;
1123 textStyle.setColor(SkiaColor(text.mFGColor, pBlend));
1125 std::string fontFamily = text.mFont;
1127 size_t pos = fontFamily.find(
'-');
1128 if (pos != std::string::npos)
1130 fontFamily = fontFamily.substr(0, pos);
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));
1139 builder->pushStyle(textStyle);
1140 builder->addText(str);
1143 auto paragraph = builder->Build();
1145 paragraph->layout(bounds.
W());
1148 switch (text.mVAlign)
1153 case EVAlign::Middle:
1154 yOffset = (bounds.
H() - paragraph->getHeight()) / 2;
1156 case EVAlign::Bottom:
1157 yOffset = bounds.
H() - paragraph->getHeight();
1162 mCanvas->translate(bounds.L, bounds.T + yOffset);
1163 paragraph->paint(mCanvas, 0, 0);
1166 DrawText(text, str, bounds, pBlend);
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";
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
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
void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override
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
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)
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 the point x, y.
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,...