iPlug2 - C++ Audio Plug-in Framework
IGraphicsCanvas.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5 
6  See LICENSE.txt for more info.
7 
8  ==============================================================================
9 */
10 
11 #include "IGraphicsCanvas.h"
12 #include <string>
13 #include <utility>
14 #include <stdio.h>
15 #include <type_traits>
16 #include <emscripten.h>
17 
18 #define STB_IMAGE_IMPLEMENTATION
19 #include "stb_image.h"
20 #include "wdl_base64.h"
21 
22 using namespace iplug;
23 using namespace igraphics;
24 using namespace emscripten;
25 
26 extern IGraphicsWeb* gGraphics;
27 
28 extern val GetPreloadedImages();
29 extern val GetCanvas();
30 
32 {
33 public:
34  Bitmap(val imageCanvas, const char* name, int scale)
35  {
36  SetBitmap(new val(imageCanvas), imageCanvas["width"].as<int>(), imageCanvas["height"].as<int>(), scale, 1.f);
37  }
38 
39  Bitmap(int width, int height, int scale, float drawScale)
40  {
41  val canvas = val::global("document").call<val>("createElement", std::string("canvas"));
42  canvas.set("width", width);
43  canvas.set("height", height);
44 
45  SetBitmap(new val(canvas), width, height, scale, drawScale);
46  }
47 
48  virtual ~Bitmap()
49  {
50  delete GetBitmap();
51  }
52 };
53 
54 struct IGraphicsCanvas::Font
55 {
56  using FontDesc = std::remove_pointer<FontDescriptor>::type;
57 
58  Font(FontDesc descriptor, double ascenderRatio, double EMRatio)
59  : mDescriptor(descriptor), mAscenderRatio(ascenderRatio), mEMRatio(EMRatio) {}
60 
61  FontDesc mDescriptor;
62  double mAscenderRatio;
63  double mEMRatio;
64 };
65 
66 static std::string GetFontString(const char* fontName, const char* styleName, double size)
67 {
68  WDL_String fontString;
69  fontString.SetFormatted(FONT_LEN + 64, "%s %lfpx %s", styleName, size, fontName);
70  return std::string(fontString.Get());
71 }
72 
73 StaticStorage<IGraphicsCanvas::Font> IGraphicsCanvas::sFontCache;
74 
75 #pragma mark - Utilities
76 
77 BEGIN_IPLUG_NAMESPACE
78 BEGIN_IGRAPHICS_NAMESPACE
79 
80 std::string CanvasColor(const IColor& color, float alpha)
81 {
82  WDL_String str;
83  str.SetFormatted(64, "rgba(%d, %d, %d, %lf)", color.R, color.G, color.B, alpha * color.A / 255.0);
84  return str.Get();
85 }
86 
87 END_IGRAPHICS_NAMESPACE
88 END_IPLUG_NAMESPACE
89 
90 #pragma mark -
91 
92 IGraphicsCanvas::IGraphicsCanvas(IGEditorDelegate& dlg, int w, int h, int fps, float scale)
93 : IGraphics(dlg, w, h, fps, scale)
94 {
95  StaticStorage<Font>::Accessor storage(sFontCache);
96  storage.Retain();
97 }
98 
99 IGraphicsCanvas::~IGraphicsCanvas()
100 {
101  StaticStorage<Font>::Accessor storage(sFontCache);
102  storage.Release();
103 }
104 
105 void IGraphicsCanvas::DrawBitmap(const IBitmap& bitmap, const IRECT& bounds, int srcX, int srcY, const IBlend* pBlend)
106 {
107  val context = GetContext();
108  val img = *bitmap.GetAPIBitmap()->GetBitmap();
109  context.call<void>("save");
110  SetCanvasBlendMode(context, pBlend);
111  context.set("globalAlpha", BlendWeight(pBlend));
112 
113  const float bs = bitmap.GetScale();
114  IRECT sr = bounds;
115  sr.Scale(bs * bitmap.GetDrawScale());
116 
117  PathRect(bounds);
118  context.call<void>("clip");
119  context.call<void>("drawImage", img, srcX * bs, srcY * bs, sr.W(), sr.H(), bounds.L, bounds.T, bounds.W(), bounds.H());
120  GetContext().call<void>("restore");
121  PathClear();
122 }
123 
125 {
126  GetContext().call<void>("beginPath");
127 }
128 
130 {
131  GetContext().call<void>("closePath");
132 }
133 
134 void IGraphicsCanvas::PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding)
135 {
136  GetContext().call<void>("arc", cx, cy, r, DegToRad(a1 - 90.f), DegToRad(a2 - 90.f), winding == EWinding::CCW);
137 }
138 
139 void IGraphicsCanvas::PathMoveTo(float x, float y)
140 {
141  GetContext().call<void>("moveTo", x, y);
142 }
143 
144 void IGraphicsCanvas::PathLineTo(float x, float y)
145 {
146  GetContext().call<void>("lineTo", x, y);
147 }
148 
149 void IGraphicsCanvas::PathCubicBezierTo(float c1x, float c1y, float c2x, float c2y, float x2, float y2)
150 {
151  GetContext().call<void>("bezierCurveTo", c1x, c1y, c2x, c2y, x2, y2);
152 }
153 
154 void IGraphicsCanvas::PathQuadraticBezierTo(float cx, float cy, float x2, float y2)
155 {
156  GetContext().call<void>("quadraticCurveTo", cx, cy, x2, y2);
157 }
158 
159 void IGraphicsCanvas::PathStroke(const IPattern& pattern, float thickness, const IStrokeOptions& options, const IBlend* pBlend)
160 {
161  val context = GetContext();
162 
163  switch (options.mCapOption)
164  {
165  case ELineCap::Butt: context.set("lineCap", "butt"); break;
166  case ELineCap::Round: context.set("lineCap", "round"); break;
167  case ELineCap::Square: context.set("lineCap", "square"); break;
168  }
169 
170  switch (options.mJoinOption)
171  {
172  case ELineJoin::Miter: context.set("lineJoin", "miter"); break;
173  case ELineJoin::Round: context.set("lineJoin", "round"); break;
174  case ELineJoin::Bevel: context.set("lineJoin", "bevel"); break;
175  }
176 
177  context.set("miterLimit", options.mMiterLimit);
178 
179  val dashArray = val::array();
180 
181  for (int i = 0; i < options.mDash.GetCount(); i++)
182  dashArray.call<void>("push", val(*(options.mDash.GetArray() + i)));
183 
184  context.call<void>("setLineDash", dashArray);
185  context.set("lineDashOffset", options.mDash.GetOffset());
186  context.set("lineWidth", thickness);
187 
188  SetCanvasSourcePattern(context, pattern, pBlend);
189 
190  context.call<void>("stroke");
191 
192  if (!options.mPreserve)
193  PathClear();
194 }
195 
196 void IGraphicsCanvas::PathFill(const IPattern& pattern, const IFillOptions& options, const IBlend* pBlend)
197 {
198  val context = GetContext();
199  std::string fillRule(options.mFillRule == EFillRule::Winding ? "nonzero" : "evenodd");
200 
201  SetCanvasSourcePattern(context, pattern, pBlend);
202 
203  context.call<void>("fill", fillRule);
204 
205  if (!options.mPreserve)
206  PathClear();
207 }
208 
209 void IGraphicsCanvas::SetCanvasSourcePattern(val& context, const IPattern& pattern, const IBlend* pBlend)
210 {
211  SetCanvasBlendMode(context, pBlend);
212 
213  switch (pattern.mType)
214  {
215  case EPatternType::Solid:
216  {
217  const IColor color = pattern.GetStop(0).mColor;
218  std::string colorString = CanvasColor(color, BlendWeight(pBlend));
219 
220  context.set("fillStyle", colorString);
221  context.set("strokeStyle", colorString);
222  }
223  break;
224 
225  case EPatternType::Linear:
226  case EPatternType::Radial:
227  {
228  double x, y;
229  IMatrix m = IMatrix(pattern.mTransform).Invert();
230  m.TransformPoint(x, y, 0.0, 1.0);
231 
232  val gradient = (pattern.mType == EPatternType::Linear) ?
233  context.call<val>("createLinearGradient", m.mTX, m.mTY, x, y) :
234  context.call<val>("createRadialGradient", m.mTX, m.mTY, 0.0, m.mTX, m.mTY, m.mXX);
235 
236  for (int i = 0; i < pattern.NStops(); i++)
237  {
238  const IColorStop& stop = pattern.GetStop(i);
239  gradient.call<void>("addColorStop", stop.mOffset, CanvasColor(stop.mColor));
240  }
241 
242  context.set("fillStyle", gradient);
243  context.set("strokeStyle", gradient);
244  }
245  break;
246  }
247 }
248 
249 void IGraphicsCanvas::SetCanvasBlendMode(val& context, const IBlend* pBlend)
250 {
251  if (!pBlend)
252  context.set("globalCompositeOperation", "source-over");
253 
254  switch (pBlend->mMethod)
255  {
256  case EBlend::SrcOver: context.set("globalCompositeOperation", "source-over"); break;
257  case EBlend::SrcIn: context.set("globalCompositeOperation", "source-in"); break;
258  case EBlend::SrcOut: context.set("globalCompositeOperation", "source-out"); break;
259  case EBlend::SrcAtop: context.set("globalCompositeOperation", "source-atop"); break;
260  case EBlend::DstOver: context.set("globalCompositeOperation", "destination-over"); break;
261  case EBlend::DstIn: context.set("globalCompositeOperation", "destination-in"); break;
262  case EBlend::DstOut: context.set("globalCompositeOperation", "destination-out"); break;
263  case EBlend::DstAtop: context.set("globalCompositeOperation", "destination-atop"); break;
264  case EBlend::Add: context.set("globalCompositeOperation", "lighter"); break;
265  case EBlend::XOR: context.set("globalCompositeOperation", "xor"); break;
266  }
267 }
268 
269 void IGraphicsCanvas::PrepareAndMeasureText(const IText& text, const char* str, IRECT& r, double& x, double & y) const
270 {
271  StaticStorage<Font>::Accessor storage(sFontCache);
272  Font* pFont = storage.Find(text.mFont);
273 
274  assert(pFont && "No font found - did you forget to load it?");
275 
276  FontDescriptor descriptor = &pFont->mDescriptor;
277  val context = GetContext();
278  std::string fontString = GetFontString(descriptor->first.Get(), descriptor->second.Get(), text.mSize * pFont->mEMRatio);
279 
280  context.set("font", fontString);
281 
282  const double textWidth = context.call<val>("measureText", std::string(str))["width"].as<double>();
283  const double textHeight = text.mSize;
284  const double ascender = pFont->mAscenderRatio * textHeight;
285  const double descender = -(1.0 - pFont->mAscenderRatio) * textHeight;
286 
287  switch (text.mAlign)
288  {
289  case EAlign::Near: x = r.L; break;
290  case EAlign::Center: x = r.MW() - (textWidth / 2.0); break;
291  case EAlign::Far: x = r.R - textWidth; break;
292  }
293 
294  switch (text.mVAlign)
295  {
296  case EVAlign::Top: y = r.T + ascender; break;
297  case EVAlign::Middle: y = r.MH() + descender + (textHeight / 2.0); break;
298  case EVAlign::Bottom: y = r.B + descender; break;
299  }
300 
301  r = IRECT((float) x, (float) (y - ascender), (float) (x + textWidth), (float) (y + textHeight - ascender));
302 }
303 
304 float IGraphicsCanvas::DoMeasureText(const IText& text, const char* str, IRECT& bounds) const
305 {
306  IRECT r = bounds;
307  double x, y;
308  PrepareAndMeasureText(text, str, bounds, x, y);
309  DoMeasureTextRotation(text, r, bounds);
310  return bounds.W();
311 }
312 
313 void IGraphicsCanvas::DoDrawText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
314 {
315  IRECT measured = bounds;
316  val context = GetContext();
317  double x, y;
318 
319  PrepareAndMeasureText(text, str, measured, x, y);
320  PathTransformSave();
321  DoTextRotation(text, bounds, measured);
322  context.set("textBaseline", std::string("alphabetic"));
323  SetCanvasSourcePattern(context, text.mFGColor, pBlend);
324  context.call<void>("fillText", std::string(str), x, y);
325  PathTransformRestore();
326 }
327 
328 void IGraphicsCanvas::PathTransformSetMatrix(const IMatrix& m)
329 {
330  const double scale = GetBackingPixelScale();
331  IMatrix t = IMatrix().Scale(scale, scale).Translate(XTranslate(), YTranslate()).Transform(m);
332 
333  GetContext().call<void>("setTransform", t.mXX, t.mYX, t.mXY, t.mYY, t.mTX, t.mTY);
334 }
335 
336 void IGraphicsCanvas::SetClipRegion(const IRECT& r)
337 {
338  val context = GetContext();
339  context.call<void>("restore");
340  context.call<void>("save");
341  context.call<void>("beginPath");
342  context.call<void>("rect", r.L, r.T, r.W(), r.H());
343  context.call<void>("clip");
344  context.call<void>("beginPath");
345 }
346 
348 {
349  char extLower[32];
350  ToLower(extLower, ext);
351  return (strstr(extLower, "png") != nullptr) || (strstr(extLower, "jpg") != nullptr) || (strstr(extLower, "jpeg") != nullptr);
352 }
353 
354 APIBitmap* IGraphicsCanvas::LoadAPIBitmap(const char* fileNameOrResID, int scale, EResourceLocation location, const char* ext)
355 {
356  return new Bitmap(GetPreloadedImages()[fileNameOrResID], fileNameOrResID + 1, scale);
357 }
358 
359 APIBitmap* IGraphicsCanvas::LoadAPIBitmap(const char* name, const void* pData, int dataSize, int scale)
360 {
361  int width = 0;
362  int height = 0;
363  int channels = 0;
364  uint8_t* pRGBA;
365 
366  pRGBA = stbi_load_from_memory((const uint8_t*)pData, dataSize, &width, &height, &channels, 4);
367  if (!pRGBA)
368  {
369  return nullptr;
370  }
371 
372  DBGMSG("Size: %d x %d\n", width, height);
373  // Now that we've decoded the data, put it on a canvas
374  int rgbaSize = width * height * channels;
375  val rgbaArray = val::global("Uint8ClampedArray").new_(val(emscripten::typed_memory_view(rgbaSize, pRGBA)));
376 
377  val imgData = val::global("ImageData").new_(rgbaArray, val(width), val(height));
378  val canvas = val::global("document").call<val>("createElement", std::string("canvas"));
379  canvas.set("width", width);
380  canvas.set("height", height);
381 
382  val ctx = canvas.call<val>("getContext", std::string("2d"));
383  ctx.call<void>("putImageData", imgData, 0, 0);
384 
385  stbi_image_free(pRGBA);
386 
387  return new Bitmap(canvas, name, scale);
388 }
389 
390 APIBitmap* IGraphicsCanvas::CreateAPIBitmap(int width, int height, int scale, double drawScale, bool cacheable)
391 {
392  return new Bitmap(width, height, scale, drawScale);
393 }
394 
395 void IGraphicsCanvas::GetFontMetrics(const char* font, const char* style, double& ascenderRatio, double& EMRatio)
396 {
397  // Provides approximate font metrics for a system font (until text metrics are properly supported)
398  int size = 1000;
399  std::string fontString = GetFontString(font, style, size);
400 
401  val document = val::global("document");
402  val textSpan = document.call<val>("createElement", std::string("span"));
403  textSpan.set("innerHTML", std::string("M"));
404  textSpan["style"].set("font", fontString);
405 
406  val block = document.call<val>("createElement", std::string("div"));
407  block["style"].set("display", std::string("inline-block"));
408  block["style"].set("width", std::string("1px"));
409  block["style"].set("height", std::string("0px"));
410 
411  val div = document.call<val>("createElement", std::string("div"));
412  div.call<void>("appendChild", textSpan);
413  div.call<void>("appendChild", block);
414  document["body"].call<void>("appendChild", div);
415 
416  block["style"].set("vertical-align", std::string("baseline"));
417  double ascent = block["offsetTop"].as<double>() - textSpan["offsetTop"].as<double>();
418  double height = textSpan.call<val>("getBoundingClientRect")["height"].as<double>();
419  document["body"].call<void>("removeChild", div);
420 
421  EMRatio = size / height;
422  ascenderRatio = ascent / height;
423 }
424 
425 bool IGraphicsCanvas::CompareFontMetrics(const char* style, const char* font1, const char* font2)
426 {
427  WDL_String fontCombination;
428  fontCombination.SetFormatted(FONT_LEN * 2 + 2, "%s, %s", font1, font2);
429  val context = GetContext();
430  std::string textString("@BmwdWMoqPYyzZr1234567890.+-=_~'");
431  const int size = 72;
432 
433  context.set("font", GetFontString(font2, style, size));
434  val metrics1 = context.call<val>("measureText", textString);
435 
436  context.set("font", GetFontString(fontCombination.Get(), style, size));
437  val metrics2 = context.call<val>("measureText", textString);
438 
439  return metrics1["width"].as<double>() == metrics2["width"].as<double>();
440 }
441 
442 bool IGraphicsCanvas::FontExists(const char* font, const char* style)
443 {
444  return !CompareFontMetrics(style, font, "monospace") || !CompareFontMetrics(style, font, "sans-serif") || !CompareFontMetrics(style, font, "serif");
445 }
446 
447 bool IGraphicsCanvas::LoadAPIFont(const char* fontID, const PlatformFontPtr& font)
448 {
449  StaticStorage<Font>::Accessor storage(sFontCache);
450 
451  if (storage.Find(fontID))
452  return true;
453 
454  if (!font->IsSystem())
455  {
456  IFontDataPtr data = font->GetFontData();
457 
458  if (data->IsValid())
459  {
460  // Load the font from data
461 
462  val fontData = val(typed_memory_view(data->GetSize(), data->Get()));
463  val fontFace = val::global("FontFace").new_(std::string(fontID), fontData);
464 
465  FontDescriptor descriptor = font->GetDescriptor();
466  const double ascenderRatio = data->GetAscender() / static_cast<double>(data->GetAscender() - data->GetDescender());
467  const double EMRatio = data->GetHeightEMRatio();
468  storage.Add(new Font({descriptor->first, descriptor->second}, ascenderRatio, EMRatio), fontID);
469 
470  // Add to store and request load immediately
471 
472  fontFace.call<val>("load");
473  val::global("document")["fonts"].call<void>("add", fontFace);
474  mLoadingFonts.push_back(fontFace);
475 
476  return true;
477  }
478  }
479  else
480  {
481  FontDescriptor descriptor = font->GetDescriptor();
482  const char* fontName = descriptor->first.Get();
483  const char* styleName = descriptor->second.Get();
484 
485  if (FontExists(fontName, styleName))
486  {
487  double ascenderRatio, EMRatio;
488 
489  GetFontMetrics(descriptor->first.Get(), descriptor->second.Get(), ascenderRatio, EMRatio);
490  storage.Add(new Font({descriptor->first, descriptor->second}, ascenderRatio, EMRatio), fontID);
491  return true;
492  }
493  }
494 
495  return false;
496 }
497 
499 {
500  for (auto it = mLoadingFonts.begin(); it != mLoadingFonts.end(); it++)
501  {
502  std::string status = (*it)["status"].as<std::string>();
503 
504  if (status.compare("loaded"))
505  {
506  assert(status.compare("error") && "Font didn't load");
507  return false;
508  }
509  }
510 
511  mLoadingFonts.clear();
512 
513  return true;
514 }
515 
516 void IGraphicsCanvas::GetLayerBitmapData(const ILayerPtr& layer, RawBitmapData& data)
517 {
518  const APIBitmap* pBitmap = layer->GetAPIBitmap();
519  int size = pBitmap->GetWidth() * pBitmap->GetHeight() * 4;
520  val context = pBitmap->GetBitmap()->call<val>("getContext", std::string("2d"));
521  val imageData = context.call<val>("getImageData", 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
522  val pixelData = imageData["data"];
523  data.Resize(size);
524 
525  // Copy pixels from context
526  if (data.GetSize() >= size)
527  {
528  unsigned char* out = data.Get();
529 
530  for (auto i = 0; i < size; i++)
531  out[i] = pixelData[i].as<unsigned char>();
532  }
533 }
534 
535 void IGraphicsCanvas::ApplyShadowMask(ILayerPtr& layer, RawBitmapData& mask, const IShadow& shadow)
536 {
537  const APIBitmap* pBitmap = layer->GetAPIBitmap();
538  int size = pBitmap->GetWidth() * pBitmap->GetHeight() * 4;
539 
540  if (mask.GetSize() >= size)
541  {
542  int width = pBitmap->GetWidth();
543  int height = pBitmap->GetHeight();
544  double scale = pBitmap->GetScale() * pBitmap->GetDrawScale();
545  double x = shadow.mXOffset * scale;
546  double y = shadow.mYOffset * scale;
547  val layerCanvas = *pBitmap->GetBitmap();
548  val layerContext = layerCanvas.call<val>("getContext", std::string("2d"));
549  layerContext.call<void>("setTransform");
550 
551  if (!shadow.mDrawForeground)
552  {
553  layerContext.call<void>("clearRect", 0, 0, width, height);
554  }
555 
556  Bitmap localBitmap(width, height, pBitmap->GetScale(), pBitmap->GetDrawScale());
557  val localCanvas = *localBitmap.GetBitmap();
558  val localContext = localCanvas.call<val>("getContext", std::string("2d"));
559  val imageData = localContext.call<val>("createImageData", width, height);
560  val pixelData = imageData["data"];
561  unsigned char* in = mask.Get();
562 
563  for (auto i = 0; i < size; i++)
564  pixelData.set(i, in[i]);
565 
566  localContext.call<void>("putImageData", imageData, 0, 0);
567  IBlend blend(EBlend::SrcIn, shadow.mOpacity);
568  localContext.call<void>("rect", 0, 0, width, height);
569  localContext.call<void>("scale", scale, scale);
570  localContext.call<void>("translate", -(layer->Bounds().L + shadow.mXOffset), -(layer->Bounds().T + shadow.mYOffset));
571  SetCanvasSourcePattern(localContext, shadow.mPattern, &blend);
572  localContext.call<void>("fill");
573 
574  layerContext.set("globalCompositeOperation", "destination-over");
575  layerContext.call<void>("drawImage", localCanvas, 0, 0, width, height, x, y, width, height);
576  }
577 }
void Scale(float scale)
Multiply each field of this IRECT by scale.
float MW() const
float MH() const
Used to manage a rectangular area, independent of draw class/platform.
Used to manage composite/blend operations, independent of draw class/platform.
User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...
Used to manage fill behaviour for path based drawing back ends.
bool LoadAPIFont(const char *fontID, const PlatformFontPtr &font) override
Drawing API method to load a font from a PlatformFontPtr, called internally.
IMatrix & Invert()
Changes the matrix to be the inverse of its original value.
int GetWidth() const
void GetLayerBitmapData(const ILayerPtr &layer, RawBitmapData &data) override
Get the contents of a layers pixels as bitmap data.
APIBitmap * CreateAPIBitmap(int width, int height, int scale, double drawScale, bool cacheable=false) override
Creates a new API bitmap, either in memory or as a GPU texture.
void PathClose() override
Close the path that is being specified.
void PathClear() override
Clear the stack of path drawing commands.
Used to manage color data, independent of draw class/platform.
Used to manage stroke behaviour for path based drawing back ends.
void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override
int GetScale() const
int GetScale() const
float R
Right side of the rectangle (X + W)
BitmapData GetBitmap() const
float H() const
An editor delegate base class for a SOMETHING that uses IGraphics for it&#39;s UI.
float W() const
int NStops() const
IText is used to manage font and text/text entry style for a piece of text on the UI...
IMatrix & Translate(float x, float y)
Set the matrix for a translation transform.
void PathLineTo(float x, float y) override
Add a line to the current path from the current point to the specified location.
const IColorStop & GetStop(int idx) const
Get the IColorStop at a particular index (will crash if out of bounds)
IGraphics platform class for the web.
Definition: IGraphicsWeb.h:43
bool BitmapExtSupported(const char *ext) override
Checks a file extension and reports whether this drawing API supports loading that extension...
APIBitmap * LoadAPIBitmap(const char *fileNameOrResID, int scale, EResourceLocation location, const char *ext) override
Drawing API method to load a bitmap, called internally.
void PathQuadraticBezierTo(float cx, float cy, float x2, float y2) override
Add a quadratic bezier to the current path from the current point to the specified location...
float DoMeasureText(const IText &text, const char *str, IRECT &bounds) const override
float GetDrawScale() const
IMatrix & Scale(float x, float y)
Set the matrix for a scale transform.
The lowest level base class of an IGraphics context.
Definition: IGraphics.h:86
float BlendWeight(const IBlend *pBlend)
Helper function to extract the blend weight value from an IBlend ptr if it is valid.
Used to specify properties of a drop-shadow to a layer.
IMatrix & Transform(const IRECT &before, const IRECT &after)
bool AssetsLoaded() override
Specialized in IGraphicsCanvas drawing backend.
Used to represent a point/stop in a gradient.
int GetHeight() const
void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options, const IBlend *pBlend) override
Stroke the current current path.
void TransformPoint(double &x, double &y, double x0, double y0) const
Transforms the point x, y.
void PathFill(const IPattern &pattern, const IFillOptions &options, const IBlend *pBlend) override
Fill the current current path.
A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...
float GetDrawScale() const
float L
Left side of the rectangle (X)
static void ToLower(char *cDest, const char *cSrc)
void PathMoveTo(float x, float y) override
Move the current point in the current path.
void DrawBitmap(const IBitmap &bitmap, const IRECT &bounds, int srcX, int srcY, const IBlend *pBlend) override
Draw a bitmap (raster) image to the graphics context.
APIBitmap * GetAPIBitmap() const
std::unique_ptr< ILayer > ILayerPtr
ILayerPtr is a managed pointer for transferring the ownership of layers.
Used to store pattern information for gradients.
Used to store transformation matrices.
void PathCubicBezierTo(float c1x, float c1y, float c2x, float c2y, float x2, float y2) override
Add a cubic bezier to the current path from the current point to the specified location.
float T
Top of the rectangle (Y)
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...
float B
Bottom of the rectangle (Y + H)