The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. // (This file gets included by juce_mac_NativeCode.mm, rather than being
  19. // compiled on its own).
  20. #if JUCE_INCLUDED_FILE
  21. //==============================================================================
  22. class CoreGraphicsImage : public Image
  23. {
  24. public:
  25. //==============================================================================
  26. CoreGraphicsImage (const PixelFormat format_,
  27. const int imageWidth_,
  28. const int imageHeight_,
  29. const bool clearImage)
  30. : Image (format_, imageWidth_, imageHeight_, clearImage)
  31. {
  32. CGColorSpaceRef colourSpace = format == Image::SingleChannel ? CGColorSpaceCreateDeviceGray()
  33. : CGColorSpaceCreateDeviceRGB();
  34. context = CGBitmapContextCreate (imageData, imageWidth, imageHeight, 8, lineStride,
  35. colourSpace, getCGImageFlags (*this));
  36. CGColorSpaceRelease (colourSpace);
  37. }
  38. ~CoreGraphicsImage()
  39. {
  40. CGContextRelease (context);
  41. }
  42. LowLevelGraphicsContext* createLowLevelContext();
  43. //==============================================================================
  44. static CGImageRef createImage (const Image& juceImage, const bool forAlpha, CGColorSpaceRef colourSpace) throw()
  45. {
  46. const CoreGraphicsImage* nativeImage = dynamic_cast <const CoreGraphicsImage*> (&juceImage);
  47. if (nativeImage != 0 && (juceImage.getFormat() == Image::SingleChannel || ! forAlpha))
  48. {
  49. return CGBitmapContextCreateImage (nativeImage->context);
  50. }
  51. else
  52. {
  53. const Image::BitmapData srcData (juceImage, 0, 0, juceImage.getWidth(), juceImage.getHeight());
  54. CGDataProviderRef provider = CGDataProviderCreateWithData (0, srcData.data, srcData.lineStride * srcData.pixelStride, 0);
  55. CGImageRef imageRef = CGImageCreate (srcData.width, srcData.height,
  56. 8, srcData.pixelStride * 8, srcData.lineStride,
  57. colourSpace, getCGImageFlags (juceImage), provider,
  58. 0, true, kCGRenderingIntentDefault);
  59. CGDataProviderRelease (provider);
  60. return imageRef;
  61. }
  62. }
  63. #if JUCE_MAC
  64. static NSImage* createNSImage (const Image& image)
  65. {
  66. const ScopedAutoReleasePool pool;
  67. NSImage* im = [[NSImage alloc] init];
  68. [im setSize: NSMakeSize (image.getWidth(), image.getHeight())];
  69. [im lockFocus];
  70. CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
  71. CGImageRef imageRef = createImage (image, false, colourSpace);
  72. CGColorSpaceRelease (colourSpace);
  73. CGContextRef cg = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
  74. CGContextDrawImage (cg, CGRectMake (0, 0, image.getWidth(), image.getHeight()), imageRef);
  75. CGImageRelease (imageRef);
  76. [im unlockFocus];
  77. return im;
  78. }
  79. #endif
  80. //==============================================================================
  81. CGContextRef context;
  82. private:
  83. static CGBitmapInfo getCGImageFlags (const Image& image) throw()
  84. {
  85. #if JUCE_BIG_ENDIAN
  86. return image.getFormat() == Image::ARGB ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big) : kCGBitmapByteOrderDefault;
  87. #else
  88. return image.getFormat() == Image::ARGB ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) : kCGBitmapByteOrderDefault;
  89. #endif
  90. }
  91. };
  92. Image* Image::createNativeImage (const PixelFormat format, const int imageWidth, const int imageHeight, const bool clearImage)
  93. {
  94. #if USE_COREGRAPHICS_RENDERING
  95. return new CoreGraphicsImage (format == RGB ? ARGB : format, imageWidth, imageHeight, clearImage);
  96. #else
  97. return new Image (format, imageWidth, imageHeight, clearImage);
  98. #endif
  99. }
  100. //==============================================================================
  101. class CoreGraphicsContext : public LowLevelGraphicsContext
  102. {
  103. public:
  104. CoreGraphicsContext (CGContextRef context_, const float flipHeight_)
  105. : context (context_),
  106. flipHeight (flipHeight_),
  107. gradientLookupTable (0),
  108. numGradientLookupEntries (0)
  109. {
  110. CGContextRetain (context);
  111. CGContextSaveGState(context);
  112. CGContextSetShouldSmoothFonts (context, true);
  113. CGContextSetShouldAntialias (context, true);
  114. CGContextSetBlendMode (context, kCGBlendModeNormal);
  115. rgbColourSpace = CGColorSpaceCreateDeviceRGB();
  116. greyColourSpace = CGColorSpaceCreateDeviceGray();
  117. gradientCallbacks.version = 0;
  118. gradientCallbacks.evaluate = gradientCallback;
  119. gradientCallbacks.releaseInfo = 0;
  120. state = new SavedState();
  121. }
  122. ~CoreGraphicsContext()
  123. {
  124. CGContextRestoreGState (context);
  125. CGContextRelease (context);
  126. CGColorSpaceRelease (rgbColourSpace);
  127. CGColorSpaceRelease (greyColourSpace);
  128. delete state;
  129. delete gradientLookupTable;
  130. }
  131. //==============================================================================
  132. bool isVectorDevice() const { return false; }
  133. void setOrigin (int x, int y)
  134. {
  135. CGContextTranslateCTM (context, x, -y);
  136. }
  137. bool clipToRectangle (const Rectangle& r)
  138. {
  139. CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()));
  140. return ! isClipEmpty();
  141. }
  142. bool clipToRectangleList (const RectangleList& clipRegion)
  143. {
  144. if (clipRegion.isEmpty())
  145. {
  146. CGContextClipToRect (context, CGRectMake (0, 0, 0, 0));
  147. return false;
  148. }
  149. else
  150. {
  151. const int numRects = clipRegion.getNumRectangles();
  152. CGRect* const rects = new CGRect [numRects];
  153. for (int i = 0; i < numRects; ++i)
  154. {
  155. const Rectangle& r = clipRegion.getRectangle(i);
  156. rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
  157. }
  158. CGContextClipToRects (context, rects, numRects);
  159. delete[] rects;
  160. return ! isClipEmpty();
  161. }
  162. }
  163. void excludeClipRectangle (const Rectangle& r)
  164. {
  165. RectangleList remaining (getClipBounds());
  166. remaining.subtract (r);
  167. clipToRectangleList (remaining);
  168. }
  169. void clipToPath (const Path& path, const AffineTransform& transform)
  170. {
  171. createPath (path, transform);
  172. CGContextClip (context);
  173. }
  174. void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform)
  175. {
  176. if (! transform.isSingularity())
  177. {
  178. Image* singleChannelImage = createAlphaChannelImage (sourceImage);
  179. CGImageRef image = CoreGraphicsImage::createImage (*singleChannelImage, true, greyColourSpace);
  180. flip();
  181. AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform));
  182. applyTransform (t);
  183. CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight());
  184. CGContextClipToMask (context, r, image);
  185. applyTransform (t.inverted());
  186. flip();
  187. CGImageRelease (image);
  188. deleteAlphaChannelImage (sourceImage, singleChannelImage);
  189. }
  190. }
  191. bool clipRegionIntersects (const Rectangle& r)
  192. {
  193. return getClipBounds().intersects (r);
  194. }
  195. const Rectangle getClipBounds() const
  196. {
  197. CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
  198. return Rectangle (roundFloatToInt (bounds.origin.x),
  199. roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
  200. roundFloatToInt (bounds.size.width),
  201. roundFloatToInt (bounds.size.height));
  202. }
  203. bool isClipEmpty() const
  204. {
  205. return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
  206. }
  207. //==============================================================================
  208. void saveState()
  209. {
  210. CGContextSaveGState (context);
  211. stateStack.add (new SavedState (*state));
  212. }
  213. void restoreState()
  214. {
  215. CGContextRestoreGState (context);
  216. SavedState* const top = stateStack.getLast();
  217. if (top != 0)
  218. {
  219. delete state;
  220. state = top;
  221. stateStack.removeLast (1, false);
  222. }
  223. else
  224. {
  225. jassertfalse // trying to pop with an empty stack!
  226. }
  227. }
  228. //==============================================================================
  229. void setFill (const FillType& fillType)
  230. {
  231. state->fillType = fillType;
  232. if (fillType.isColour())
  233. {
  234. CGContextSetRGBFillColor (context, fillType.colour.getFloatRed(), fillType.colour.getFloatGreen(),
  235. fillType.colour.getFloatBlue(), fillType.colour.getFloatAlpha());
  236. CGContextSetAlpha (context, 1.0f);
  237. }
  238. }
  239. void setOpacity (float newOpacity)
  240. {
  241. state->fillType.setOpacity (newOpacity);
  242. setFill (state->fillType);
  243. }
  244. void setInterpolationQuality (Graphics::ResamplingQuality quality)
  245. {
  246. CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality
  247. ? kCGInterpolationLow
  248. : kCGInterpolationHigh);
  249. }
  250. //==============================================================================
  251. void fillRect (const Rectangle& r, const bool replaceExistingContents)
  252. {
  253. CGRect cgRect = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
  254. if (replaceExistingContents)
  255. {
  256. #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
  257. CGContextClearRect (context, cgRect);
  258. #else
  259. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  260. if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later)
  261. CGContextClearRect (context, cgRect);
  262. else
  263. #endif
  264. CGContextSetBlendMode (context, kCGBlendModeCopy);
  265. #endif
  266. fillRect (r, false);
  267. CGContextSetBlendMode (context, kCGBlendModeNormal);
  268. }
  269. else
  270. {
  271. if (state->fillType.isColour())
  272. {
  273. CGContextFillRect (context, cgRect);
  274. }
  275. else if (state->fillType.isGradient())
  276. {
  277. CGContextSaveGState (context);
  278. CGContextClipToRect (context, cgRect);
  279. drawGradient();
  280. CGContextRestoreGState (context);
  281. }
  282. else
  283. {
  284. CGContextSaveGState (context);
  285. CGContextClipToRect (context, cgRect);
  286. drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true);
  287. CGContextRestoreGState (context);
  288. }
  289. }
  290. }
  291. void fillPath (const Path& path, const AffineTransform& transform)
  292. {
  293. CGContextSaveGState (context);
  294. if (state->fillType.isColour())
  295. {
  296. flip();
  297. applyTransform (transform);
  298. createPath (path);
  299. if (path.isUsingNonZeroWinding())
  300. CGContextFillPath (context);
  301. else
  302. CGContextEOFillPath (context);
  303. }
  304. else
  305. {
  306. createPath (path, transform);
  307. if (path.isUsingNonZeroWinding())
  308. CGContextClip (context);
  309. else
  310. CGContextEOClip (context);
  311. if (state->fillType.isGradient())
  312. drawGradient();
  313. else
  314. drawImage (*(state->fillType.image), state->fillType.image->getBounds(), state->fillType.transform, true);
  315. }
  316. CGContextRestoreGState (context);
  317. }
  318. void drawImage (const Image& sourceImage, const Rectangle& srcClip,
  319. const AffineTransform& transform, const bool fillEntireClipAsTiles)
  320. {
  321. jassert (sourceImage.getBounds().contains (srcClip));
  322. CGImageRef fullImage = CoreGraphicsImage::createImage (sourceImage, false, rgbColourSpace);
  323. CGImageRef image = fullImage;
  324. if (srcClip != sourceImage.getBounds())
  325. {
  326. image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), srcClip.getY(),
  327. srcClip.getWidth(), srcClip.getHeight()));
  328. CGImageRelease (fullImage);
  329. }
  330. CGContextSaveGState (context);
  331. CGContextSetAlpha (context, state->fillType.getOpacity());
  332. flip();
  333. applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, srcClip.getHeight()).followedBy (transform));
  334. CGRect imageRect = CGRectMake (0, 0, srcClip.getWidth(), srcClip.getHeight());
  335. if (fillEntireClipAsTiles)
  336. {
  337. #if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
  338. CGContextDrawTiledImage (context, imageRect, image);
  339. #else
  340. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  341. if (CGContextDrawTiledImage != 0)
  342. CGContextDrawTiledImage (context, imageRect, image);
  343. else
  344. #endif
  345. {
  346. // Fallback to manually doing a tiled fill on 10.4
  347. CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context));
  348. const int iw = srcClip.getWidth();
  349. const int ih = srcClip.getHeight();
  350. int x = 0, y = 0;
  351. while (x > clip.origin.x) x -= iw;
  352. while (y > clip.origin.y) y -= ih;
  353. const int right = (int) (clip.origin.x + clip.size.width);
  354. const int bottom = (int) (clip.origin.y + clip.size.height);
  355. while (y < bottom)
  356. {
  357. for (int x2 = x; x2 < right; x2 += iw)
  358. CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image);
  359. y += ih;
  360. }
  361. }
  362. #endif
  363. }
  364. else
  365. {
  366. CGContextDrawImage (context, imageRect, image);
  367. }
  368. CGImageRelease (image); // (This causes a memory bug in iPhone sim 3.0 - try upgrading to a later version if you hit this)
  369. CGContextRestoreGState (context);
  370. }
  371. //==============================================================================
  372. void drawLine (double x1, double y1, double x2, double y2)
  373. {
  374. CGContextSetLineCap (context, kCGLineCapSquare);
  375. CGContextSetLineWidth (context, 1.0f);
  376. CGContextSetRGBStrokeColor (context,
  377. state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(),
  378. state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha());
  379. CGPoint line[] = { { (float) x1 + 0.5f, flipHeight - ((float) y1 + 0.5f) },
  380. { (float) x2 + 0.5f, flipHeight - ((float) y2 + 0.5f) } };
  381. CGContextStrokeLineSegments (context, line, 1);
  382. }
  383. void drawVerticalLine (const int x, double top, double bottom)
  384. {
  385. CGContextFillRect (context, CGRectMake (x, flipHeight - (float) bottom, 1.0f, (float) (bottom - top)));
  386. }
  387. void drawHorizontalLine (const int y, double left, double right)
  388. {
  389. CGContextFillRect (context, CGRectMake ((float) left, flipHeight - (y + 1.0f), (float) (right - left), 1.0f));
  390. }
  391. void setFont (const Font& newFont)
  392. {
  393. if (state->font != newFont)
  394. {
  395. state->fontRef = 0;
  396. state->font = newFont;
  397. MacTypeface* mf = dynamic_cast <MacTypeface*> ((Typeface*) state->font.getTypeface());
  398. if (mf != 0)
  399. {
  400. state->fontRef = mf->fontRef;
  401. CGContextSetFont (context, state->fontRef);
  402. CGContextSetFontSize (context, state->font.getHeight() * mf->fontHeightToCGSizeFactor);
  403. state->fontTransform = mf->renderingTransform;
  404. state->fontTransform.a *= state->font.getHorizontalScale();
  405. CGContextSetTextMatrix (context, state->fontTransform);
  406. }
  407. }
  408. }
  409. const Font getFont()
  410. {
  411. return state->font;
  412. }
  413. void drawGlyph (int glyphNumber, const AffineTransform& transform)
  414. {
  415. if (state->fontRef != 0 && state->fillType.isColour())
  416. {
  417. if (transform.isOnlyTranslation())
  418. {
  419. CGGlyph g = glyphNumber;
  420. CGContextShowGlyphsAtPoint (context, transform.getTranslationX(),
  421. flipHeight - roundFloatToInt (transform.getTranslationY()), &g, 1);
  422. }
  423. else
  424. {
  425. CGContextSaveGState (context);
  426. flip();
  427. applyTransform (transform);
  428. CGAffineTransform t = state->fontTransform;
  429. t.d = -t.d;
  430. CGContextSetTextMatrix (context, t);
  431. CGGlyph g = glyphNumber;
  432. CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1);
  433. CGContextSetTextMatrix (context, state->fontTransform);
  434. CGContextRestoreGState (context);
  435. }
  436. }
  437. else
  438. {
  439. Path p;
  440. Font& f = state->font;
  441. f.getTypeface()->getOutlineForGlyph (glyphNumber, p);
  442. fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight())
  443. .followedBy (transform));
  444. }
  445. }
  446. private:
  447. CGContextRef context;
  448. const float flipHeight;
  449. CGColorSpaceRef rgbColourSpace, greyColourSpace;
  450. CGFunctionCallbacks gradientCallbacks;
  451. struct SavedState
  452. {
  453. SavedState() throw()
  454. : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity)
  455. {
  456. }
  457. SavedState (const SavedState& other) throw()
  458. : fillType (other.fillType), font (other.font), fontRef (other.fontRef),
  459. fontTransform (other.fontTransform)
  460. {
  461. }
  462. ~SavedState() throw()
  463. {
  464. }
  465. FillType fillType;
  466. Font font;
  467. CGFontRef fontRef;
  468. CGAffineTransform fontTransform;
  469. };
  470. SavedState* state;
  471. OwnedArray <SavedState> stateStack;
  472. PixelARGB* gradientLookupTable;
  473. int numGradientLookupEntries;
  474. static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData)
  475. {
  476. const CoreGraphicsContext* const g = (const CoreGraphicsContext*) info;
  477. const int index = roundFloatToInt (g->numGradientLookupEntries * inData[0]);
  478. PixelARGB colour (g->gradientLookupTable [jlimit (0, g->numGradientLookupEntries, index)]);
  479. colour.unpremultiply();
  480. outData[0] = colour.getRed() / 255.0f;
  481. outData[1] = colour.getGreen() / 255.0f;
  482. outData[2] = colour.getBlue() / 255.0f;
  483. outData[3] = colour.getAlpha() / 255.0f;
  484. }
  485. CGShadingRef createGradient (const AffineTransform& transform, ColourGradient gradient) throw()
  486. {
  487. delete gradientLookupTable;
  488. gradientLookupTable = gradient.createLookupTable (transform, numGradientLookupEntries);
  489. --numGradientLookupEntries;
  490. CGShadingRef result = 0;
  491. CGFunctionRef function = CGFunctionCreate ((void*) this, 1, 0, 4, 0, &gradientCallbacks);
  492. CGPoint p1 (CGPointMake (gradient.x1, gradient.y1));
  493. if (gradient.isRadial)
  494. {
  495. result = CGShadingCreateRadial (rgbColourSpace, p1, 0,
  496. p1, hypotf (gradient.x1 - gradient.x2, gradient.y1 - gradient.y2),
  497. function, true, true);
  498. }
  499. else
  500. {
  501. result = CGShadingCreateAxial (rgbColourSpace, p1,
  502. CGPointMake (gradient.x2, gradient.y2),
  503. function, true, true);
  504. }
  505. CGFunctionRelease (function);
  506. return result;
  507. }
  508. void drawGradient() throw()
  509. {
  510. flip();
  511. applyTransform (state->fillType.transform);
  512. CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if
  513. // you draw a gradient with high quality interp enabled).
  514. CGShadingRef shading = createGradient (state->fillType.transform, *(state->fillType.gradient));
  515. CGContextSetAlpha (context, state->fillType.getOpacity());
  516. CGContextDrawShading (context, shading);
  517. CGShadingRelease (shading);
  518. }
  519. void createPath (const Path& path) const throw()
  520. {
  521. CGContextBeginPath (context);
  522. Path::Iterator i (path);
  523. while (i.next())
  524. {
  525. switch (i.elementType)
  526. {
  527. case Path::Iterator::startNewSubPath:
  528. CGContextMoveToPoint (context, i.x1, i.y1);
  529. break;
  530. case Path::Iterator::lineTo:
  531. CGContextAddLineToPoint (context, i.x1, i.y1);
  532. break;
  533. case Path::Iterator::quadraticTo:
  534. CGContextAddQuadCurveToPoint (context, i.x1, i.y1, i.x2, i.y2);
  535. break;
  536. case Path::Iterator::cubicTo:
  537. CGContextAddCurveToPoint (context, i.x1, i.y1, i.x2, i.y2, i.x3, i.y3);
  538. break;
  539. case Path::Iterator::closePath:
  540. CGContextClosePath (context); break;
  541. default:
  542. jassertfalse
  543. break;
  544. }
  545. }
  546. }
  547. void createPath (const Path& path, const AffineTransform& transform) const throw()
  548. {
  549. CGContextBeginPath (context);
  550. Path::Iterator i (path);
  551. while (i.next())
  552. {
  553. switch (i.elementType)
  554. {
  555. case Path::Iterator::startNewSubPath:
  556. transform.transformPoint (i.x1, i.y1);
  557. CGContextMoveToPoint (context, i.x1, flipHeight - i.y1);
  558. break;
  559. case Path::Iterator::lineTo:
  560. transform.transformPoint (i.x1, i.y1);
  561. CGContextAddLineToPoint (context, i.x1, flipHeight - i.y1);
  562. break;
  563. case Path::Iterator::quadraticTo:
  564. transform.transformPoint (i.x1, i.y1);
  565. transform.transformPoint (i.x2, i.y2);
  566. CGContextAddQuadCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2);
  567. break;
  568. case Path::Iterator::cubicTo:
  569. transform.transformPoint (i.x1, i.y1);
  570. transform.transformPoint (i.x2, i.y2);
  571. transform.transformPoint (i.x3, i.y3);
  572. CGContextAddCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2, i.x3, flipHeight - i.y3);
  573. break;
  574. case Path::Iterator::closePath:
  575. CGContextClosePath (context); break;
  576. default:
  577. jassertfalse
  578. break;
  579. }
  580. }
  581. }
  582. static Image* createAlphaChannelImage (const Image& im) throw()
  583. {
  584. if (im.getFormat() == Image::SingleChannel)
  585. return const_cast <Image*> (&im);
  586. return im.createCopyOfAlphaChannel();
  587. }
  588. static void deleteAlphaChannelImage (const Image& im, Image* const alphaIm) throw()
  589. {
  590. if (im.getFormat() != Image::SingleChannel)
  591. delete alphaIm;
  592. }
  593. void flip() const throw()
  594. {
  595. CGContextConcatCTM (context, CGAffineTransformMake (1, 0, 0, -1, 0, flipHeight));
  596. }
  597. void applyTransform (const AffineTransform& transform) const throw()
  598. {
  599. CGAffineTransform t;
  600. t.a = transform.mat00;
  601. t.b = transform.mat10;
  602. t.c = transform.mat01;
  603. t.d = transform.mat11;
  604. t.tx = transform.mat02;
  605. t.ty = transform.mat12;
  606. CGContextConcatCTM (context, t);
  607. }
  608. float flipY (float y) const throw()
  609. {
  610. return flipHeight - y;
  611. }
  612. };
  613. LowLevelGraphicsContext* CoreGraphicsImage::createLowLevelContext()
  614. {
  615. return new CoreGraphicsContext (context, imageHeight);
  616. }
  617. #endif