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.

932 lines
36KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. namespace GraphicsHelpers
  19. {
  20. jobject createPaint (Graphics::ResamplingQuality quality)
  21. {
  22. jint constructorFlags = 1 /*ANTI_ALIAS_FLAG*/
  23. | 4 /*DITHER_FLAG*/
  24. | 128 /*SUBPIXEL_TEXT_FLAG*/;
  25. if (quality > Graphics::lowResamplingQuality)
  26. constructorFlags |= 2; /*FILTER_BITMAP_FLAG*/
  27. return getEnv()->NewObject (Paint, Paint.constructor, constructorFlags);
  28. }
  29. const jobject createMatrix (JNIEnv* env, const AffineTransform& t)
  30. {
  31. jobject m = env->NewObject (Matrix, Matrix.constructor);
  32. jfloat values[9] = { t.mat00, t.mat01, t.mat02,
  33. t.mat10, t.mat11, t.mat12,
  34. 0.0f, 0.0f, 1.0f };
  35. jfloatArray javaArray = env->NewFloatArray (9);
  36. env->SetFloatArrayRegion (javaArray, 0, 9, values);
  37. env->CallVoidMethod (m, Matrix.setValues, javaArray);
  38. env->DeleteLocalRef (javaArray);
  39. return m;
  40. }
  41. }
  42. #if USE_ANDROID_CANVAS
  43. //==============================================================================
  44. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  45. METHOD (constructor, "<init>", "(Landroid/graphics/Bitmap;)V") \
  46. METHOD (drawRect, "drawRect", "(FFFFLandroid/graphics/Paint;)V") \
  47. METHOD (translate, "translate", "(FF)V") \
  48. METHOD (clipPath, "clipPath", "(Landroid/graphics/Path;)Z") \
  49. METHOD (clipRect, "clipRect", "(FFFF)Z") \
  50. METHOD (clipRegion, "clipRegion", "(Landroid/graphics/Region;)Z") \
  51. METHOD (concat, "concat", "(Landroid/graphics/Matrix;)V") \
  52. METHOD (drawBitmap, "drawBitmap", "(Landroid/graphics/Bitmap;Landroid/graphics/Matrix;Landroid/graphics/Paint;)V") \
  53. METHOD (drawBitmapAt, "drawBitmap", "(Landroid/graphics/Bitmap;FFLandroid/graphics/Paint;)V") \
  54. METHOD (drawMemoryBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \
  55. METHOD (drawLine, "drawLine", "(FFFFLandroid/graphics/Paint;)V") \
  56. METHOD (drawPath, "drawPath", "(Landroid/graphics/Path;Landroid/graphics/Paint;)V") \
  57. METHOD (drawText, "drawText", "(Ljava/lang/String;FFLandroid/graphics/Paint;)V") \
  58. METHOD (getClipBounds, "getClipBounds", "(Landroid/graphics/Rect;)Z") \
  59. METHOD (getClipBounds2, "getClipBounds", "()Landroid/graphics/Rect;") \
  60. METHOD (getMatrix, "getMatrix", "()Landroid/graphics/Matrix;") \
  61. METHOD (save, "save", "()I") \
  62. METHOD (restore, "restore", "()V") \
  63. METHOD (saveLayerAlpha, "saveLayerAlpha", "(FFFFII)I")
  64. DECLARE_JNI_CLASS (Canvas, "android/graphics/Canvas");
  65. #undef JNI_CLASS_MEMBERS
  66. //==============================================================================
  67. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  68. METHOD (constructor, "<init>", "()V") \
  69. METHOD (moveTo, "moveTo", "(FF)V") \
  70. METHOD (lineTo, "lineTo", "(FF)V") \
  71. METHOD (quadTo, "quadTo", "(FFFF)V") \
  72. METHOD (cubicTo, "cubicTo", "(FFFFFF)V") \
  73. METHOD (closePath, "close", "()V") \
  74. METHOD (computeBounds, "computeBounds", "(Landroid/graphics/RectF;Z)V") \
  75. DECLARE_JNI_CLASS (PathClass, "android/graphics/Path");
  76. #undef JNI_CLASS_MEMBERS
  77. //==============================================================================
  78. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  79. METHOD (constructor, "<init>", "()V"); \
  80. METHOD (regionUnion, "union", "(Landroid/graphics/Rect;)Z"); \
  81. DECLARE_JNI_CLASS (RegionClass, "android/graphics/Region");
  82. #undef JNI_CLASS_MEMBERS
  83. //==============================================================================
  84. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  85. STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
  86. METHOD (bitmapCopy, "copy", "(Landroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;") \
  87. METHOD (getPixels, "getPixels", "([IIIIIII)V") \
  88. METHOD (setPixels, "setPixels", "([IIIIIII)V") \
  89. METHOD (recycle, "recycle", "()V") \
  90. DECLARE_JNI_CLASS (BitmapClass, "android/graphics/Bitmap");
  91. #undef JNI_CLASS_MEMBERS
  92. //==============================================================================
  93. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  94. STATICFIELD (ARGB_8888, "ARGB_8888", "Landroid/graphics/Bitmap$Config;") \
  95. STATICFIELD (ALPHA_8, "ALPHA_8", "Landroid/graphics/Bitmap$Config;") \
  96. DECLARE_JNI_CLASS (BitmapConfig, "android/graphics/Bitmap$Config");
  97. #undef JNI_CLASS_MEMBERS
  98. //==============================================================================
  99. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  100. METHOD (constructor, "<init>", "(Landroid/graphics/Bitmap;Landroid/graphics/Shader$TileMode;Landroid/graphics/Shader$TileMode;)V")
  101. DECLARE_JNI_CLASS (BitmapShader, "android/graphics/BitmapShader");
  102. #undef JNI_CLASS_MEMBERS
  103. //==============================================================================
  104. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  105. METHOD (setLocalMatrix, "setLocalMatrix", "(Landroid/graphics/Matrix;)V")
  106. DECLARE_JNI_CLASS (ShaderClass, "android/graphics/Shader");
  107. #undef JNI_CLASS_MEMBERS
  108. //==============================================================================
  109. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  110. STATICFIELD (CLAMP, "CLAMP", "Landroid/graphics/Shader$TileMode;")
  111. DECLARE_JNI_CLASS (ShaderTileMode, "android/graphics/Shader$TileMode");
  112. #undef JNI_CLASS_MEMBERS
  113. //==============================================================================
  114. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  115. METHOD (constructor, "<init>", "(FFFF[I[FLandroid/graphics/Shader$TileMode;)V") \
  116. DECLARE_JNI_CLASS (LinearGradientClass, "android/graphics/LinearGradient");
  117. #undef JNI_CLASS_MEMBERS
  118. //==============================================================================
  119. #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
  120. METHOD (constructor, "<init>", "(FFF[I[FLandroid/graphics/Shader$TileMode;)V") \
  121. DECLARE_JNI_CLASS (RadialGradientClass, "android/graphics/RadialGradient");
  122. #undef JNI_CLASS_MEMBERS
  123. //==============================================================================
  124. class AndroidImage : public Image::SharedImage
  125. {
  126. public:
  127. //==============================================================================
  128. AndroidImage (const int width_, const int height_, const bool clearImage)
  129. : Image::SharedImage (Image::ARGB, width_, height_),
  130. bitmap (createBitmap (width_, height_, false))
  131. {
  132. }
  133. AndroidImage (const int width_, const int height_, const GlobalRef& bitmap_)
  134. : Image::SharedImage (Image::ARGB, width_, height_),
  135. bitmap (bitmap_)
  136. {
  137. }
  138. ~AndroidImage()
  139. {
  140. if (bitmap != 0)
  141. bitmap.callVoidMethod (BitmapClass.recycle);
  142. }
  143. Image::ImageType getType() const { return Image::NativeImage; }
  144. LowLevelGraphicsContext* createLowLevelContext();
  145. void initialiseBitmapData (Image::BitmapData& bm, int x, int y, Image::BitmapData::ReadWriteMode mode)
  146. {
  147. bm.lineStride = width * sizeof (jint);
  148. bm.pixelStride = sizeof (jint);
  149. bm.pixelFormat = Image::ARGB;
  150. bm.dataReleaser = new CopyHandler (*this, bm, x, y, mode);
  151. }
  152. SharedImage* clone()
  153. {
  154. JNIEnv* env = getEnv();
  155. jobject mode = env->GetStaticObjectField (BitmapConfig, BitmapConfig.ARGB_8888);
  156. GlobalRef newCopy (bitmap.callObjectMethod (BitmapClass.bitmapCopy, mode, true));
  157. env->DeleteLocalRef (mode);
  158. return new AndroidImage (width, height, newCopy);
  159. }
  160. static jobject createBitmap (int width, int height, bool asSingleChannel)
  161. {
  162. JNIEnv* env = getEnv();
  163. jobject mode = env->GetStaticObjectField (BitmapConfig, asSingleChannel ? BitmapConfig.ALPHA_8
  164. : BitmapConfig.ARGB_8888);
  165. jobject result = env->CallStaticObjectMethod (BitmapClass, BitmapClass.createBitmap, width, height, mode);
  166. env->DeleteLocalRef (mode);
  167. return result;
  168. }
  169. //==============================================================================
  170. GlobalRef bitmap;
  171. private:
  172. class CopyHandler : public Image::BitmapData::BitmapDataReleaser
  173. {
  174. public:
  175. CopyHandler (AndroidImage& owner_, Image::BitmapData& bitmapData_,
  176. const int x_, const int y_, const Image::BitmapData::ReadWriteMode mode_)
  177. : owner (owner_), bitmapData (bitmapData_), mode (mode_), x (x_), y (y_)
  178. {
  179. JNIEnv* env = getEnv();
  180. intArray = env->NewIntArray (bitmapData.width * bitmapData.height);
  181. if (mode != Image::BitmapData::writeOnly)
  182. owner_.bitmap.callVoidMethod (BitmapClass.getPixels, intArray, 0, bitmapData.width, x_, y_,
  183. bitmapData.width, bitmapData.height);
  184. bitmapData.data = (uint8*) env->GetIntArrayElements (intArray, 0);
  185. if (mode != Image::BitmapData::writeOnly)
  186. {
  187. for (int yy = 0; yy < bitmapData.height; ++yy)
  188. {
  189. PixelARGB* p = (PixelARGB*) bitmapData.getLinePointer (yy);
  190. for (int xx = 0; xx < bitmapData.width; ++xx)
  191. p[xx].premultiply();
  192. }
  193. }
  194. }
  195. ~CopyHandler()
  196. {
  197. JNIEnv* env = getEnv();
  198. if (mode != Image::BitmapData::readOnly)
  199. {
  200. for (int yy = 0; yy < bitmapData.height; ++yy)
  201. {
  202. PixelARGB* p = (PixelARGB*) bitmapData.getLinePointer (yy);
  203. for (int xx = 0; xx < bitmapData.width; ++xx)
  204. p[xx].unpremultiply();
  205. }
  206. }
  207. env->ReleaseIntArrayElements (intArray, (jint*) bitmapData.data, 0);
  208. if (mode != Image::BitmapData::readOnly)
  209. owner.bitmap.callVoidMethod (BitmapClass.setPixels, intArray, 0, bitmapData.width, x, y,
  210. bitmapData.width, bitmapData.height);
  211. env->DeleteLocalRef (intArray);
  212. }
  213. private:
  214. AndroidImage& owner;
  215. Image::BitmapData& bitmapData;
  216. jintArray intArray;
  217. const Image::BitmapData::ReadWriteMode mode;
  218. const int x, y;
  219. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CopyHandler);
  220. };
  221. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidImage);
  222. };
  223. #endif
  224. Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage)
  225. {
  226. #if USE_ANDROID_CANVAS
  227. if (format != Image::SingleChannel)
  228. return new AndroidImage (width, height, clearImage);
  229. #endif
  230. return createSoftwareImage (format, width, height, clearImage);
  231. }
  232. #if USE_ANDROID_CANVAS
  233. //==============================================================================
  234. class AndroidLowLevelGraphicsContext : public LowLevelGraphicsContext
  235. {
  236. public:
  237. AndroidLowLevelGraphicsContext (jobject canvas_)
  238. : originalCanvas (canvas_),
  239. currentState (new SavedState (canvas_))
  240. {
  241. setFill (Colours::black);
  242. }
  243. ~AndroidLowLevelGraphicsContext()
  244. {
  245. while (stateStack.size() > 0)
  246. restoreState();
  247. currentState->flattenImageClippingLayer (originalCanvas);
  248. }
  249. bool isVectorDevice() const { return false; }
  250. //==============================================================================
  251. void setOrigin (int x, int y)
  252. {
  253. getCanvas().callVoidMethod (Canvas.translate, (float) x, (float) y);
  254. }
  255. void addTransform (const AffineTransform& transform)
  256. {
  257. getCanvas().callVoidMethod (Canvas.concat, createMatrixRef (getEnv(), transform).get());
  258. }
  259. float getScaleFactor()
  260. {
  261. return 1.0f;
  262. }
  263. bool clipToRectangle (const Rectangle<int>& r)
  264. {
  265. return getCanvas().callBooleanMethod (Canvas.clipRect, (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom());
  266. }
  267. bool clipToRectangleList (const RectangleList& clipRegion)
  268. {
  269. RectangleList excluded (getClipBounds());
  270. excluded.subtract (clipRegion);
  271. const int numRects = excluded.getNumRectangles();
  272. for (int i = 0; i < numRects; ++i)
  273. excludeClipRectangle (excluded.getRectangle(i));
  274. }
  275. void excludeClipRectangle (const Rectangle<int>& r)
  276. {
  277. android.activity.callVoidMethod (JuceAppActivity.excludeClipRegion, getCanvas().get(),
  278. (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom());
  279. }
  280. void clipToPath (const Path& path, const AffineTransform& transform)
  281. {
  282. (void) getCanvas().callBooleanMethod (Canvas.clipPath, createPath (getEnv(), path, transform).get());
  283. }
  284. void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
  285. {
  286. // XXX couldn't get image clipping to work...
  287. JNIEnv* env = getEnv();
  288. {
  289. Path p;
  290. p.addRectangle (sourceImage.getBounds().toFloat());
  291. clipToPath (p, transform);
  292. }
  293. Rectangle<int> bounds (getClipBounds());
  294. jobject temporaryLayerBitmap = AndroidImage::createBitmap (bounds.getWidth(), bounds.getHeight(), false);
  295. jobject temporaryCanvas = env->NewObject (Canvas, Canvas.constructor, temporaryLayerBitmap);
  296. setFill (Colours::red);
  297. env->CallVoidMethod (temporaryCanvas, Canvas.drawRect,
  298. (jfloat) 20, (jfloat) 20, (jfloat) 300, (jfloat) 200,
  299. getCurrentPaint());
  300. env->CallVoidMethod (temporaryCanvas, Canvas.translate,
  301. (jfloat) -bounds.getX(), (jfloat) -bounds.getY());
  302. Image maskImage (Image::SingleChannel, bounds.getWidth(), bounds.getHeight(), true);
  303. {
  304. Graphics g (maskImage);
  305. g.setOrigin (-bounds.getWidth(), -bounds.getHeight());
  306. g.drawImageTransformed (sourceImage, transform);
  307. }
  308. SavedState* const top = stateStack.getLast();
  309. currentState->clipToImage (top != nullptr ? top->canvas.get() : originalCanvas,
  310. temporaryCanvas, temporaryLayerBitmap, maskImage,
  311. bounds.getX(), bounds.getY());
  312. }
  313. bool clipRegionIntersects (const Rectangle<int>& r)
  314. {
  315. return getClipBounds().intersects (r);
  316. }
  317. Rectangle<int> getClipBounds() const
  318. {
  319. JNIEnv* env = getEnv();
  320. jobject rect = getCanvas().callObjectMethod (Canvas.getClipBounds2);
  321. const int left = env->GetIntField (rect, RectClass.left);
  322. const int top = env->GetIntField (rect, RectClass.top);
  323. const int right = env->GetIntField (rect, RectClass.right);
  324. const int bottom = env->GetIntField (rect, RectClass.bottom);
  325. env->DeleteLocalRef (rect);
  326. return Rectangle<int> (left, top, right - left, bottom - top);
  327. }
  328. bool isClipEmpty() const
  329. {
  330. LocalRef<jobject> tempRect (getEnv()->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0));
  331. return ! getCanvas().callBooleanMethod (Canvas.getClipBounds, tempRect.get());
  332. }
  333. //==============================================================================
  334. void setFill (const FillType& fillType)
  335. {
  336. currentState->setFillType (fillType);
  337. }
  338. void setOpacity (float newOpacity)
  339. {
  340. currentState->setAlpha (newOpacity);
  341. }
  342. void setInterpolationQuality (Graphics::ResamplingQuality quality)
  343. {
  344. currentState->setInterpolationQuality (quality);
  345. }
  346. //==============================================================================
  347. void fillRect (const Rectangle<int>& r, bool replaceExistingContents)
  348. {
  349. getCanvas().callVoidMethod (Canvas.drawRect,
  350. (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom(),
  351. getCurrentPaint());
  352. }
  353. void fillPath (const Path& path, const AffineTransform& transform)
  354. {
  355. getCanvas().callVoidMethod (Canvas.drawPath, createPath (getEnv(), path, transform).get(),
  356. getCurrentPaint());
  357. }
  358. void drawImage (const Image& sourceImage, const AffineTransform& transform, bool fillEntireClipAsTiles)
  359. {
  360. AndroidImage* androidImage = dynamic_cast <AndroidImage*> (sourceImage.getSharedImage());
  361. if (androidImage != 0)
  362. {
  363. JNIEnv* env = getEnv();
  364. getCanvas().callVoidMethod (Canvas.drawBitmap, androidImage->bitmap.get(),
  365. createMatrixRef (env, transform).get(), getImagePaint());
  366. }
  367. else
  368. {
  369. if (transform.isOnlyTranslation())
  370. {
  371. JNIEnv* env = getEnv();
  372. Image::BitmapData bm (sourceImage, Image::BitmapData::readOnly);
  373. jintArray imageData = env->NewIntArray (bm.width * bm.height);
  374. jint* dest = env->GetIntArrayElements (imageData, 0);
  375. if (dest != 0)
  376. {
  377. const uint8* srcLine = bm.getLinePointer (0);
  378. jint* dstLine = dest;
  379. for (int y = 0; y < bm.height; ++y)
  380. {
  381. switch (bm.pixelFormat)
  382. {
  383. case Image::ARGB: copyPixels (dstLine, (PixelARGB*) srcLine, bm.width, bm.pixelStride); break;
  384. case Image::RGB: copyPixels (dstLine, (PixelRGB*) srcLine, bm.width, bm.pixelStride); break;
  385. case Image::SingleChannel: copyPixels (dstLine, (PixelAlpha*) srcLine, bm.width, bm.pixelStride); break;
  386. default: jassertfalse; break;
  387. }
  388. srcLine += bm.lineStride;
  389. dstLine += bm.width;
  390. }
  391. env->ReleaseIntArrayElements (imageData, dest, 0);
  392. getCanvas().callVoidMethod (Canvas.drawMemoryBitmap, imageData, 0, bm.width,
  393. transform.getTranslationX(), transform.getTranslationY(),
  394. bm.width, bm.height, true, getImagePaint());
  395. env->DeleteLocalRef (imageData);
  396. }
  397. }
  398. else
  399. {
  400. saveState();
  401. addTransform (transform);
  402. drawImage (sourceImage, AffineTransform::identity, fillEntireClipAsTiles);
  403. restoreState();
  404. }
  405. }
  406. }
  407. void drawLine (const Line <float>& line)
  408. {
  409. getCanvas().callVoidMethod (Canvas.drawLine, line.getStartX(), line.getStartY(),
  410. line.getEndX(), line.getEndY(), getCurrentPaint());
  411. }
  412. void drawVerticalLine (int x, float top, float bottom)
  413. {
  414. getCanvas().callVoidMethod (Canvas.drawRect, (float) x, top, x + 1.0f, bottom, getCurrentPaint());
  415. }
  416. void drawHorizontalLine (int y, float left, float right)
  417. {
  418. getCanvas().callVoidMethod (Canvas.drawRect, left, (float) y, right, y + 1.0f, getCurrentPaint());
  419. }
  420. void setFont (const Font& newFont)
  421. {
  422. if (currentState->font != newFont)
  423. {
  424. currentState->font = newFont;
  425. currentState->typefaceNeedsUpdate = true;
  426. }
  427. }
  428. Font getFont()
  429. {
  430. return currentState->font;
  431. }
  432. void drawGlyph (int glyphNumber, const AffineTransform& transform)
  433. {
  434. if (transform.isOnlyTranslation())
  435. {
  436. getCanvas().callVoidMethod (Canvas.drawText, javaStringFromChar ((juce_wchar) glyphNumber).get(),
  437. transform.getTranslationX(), transform.getTranslationY(),
  438. currentState->getPaintForTypeface());
  439. }
  440. else
  441. {
  442. saveState();
  443. addTransform (transform);
  444. drawGlyph (glyphNumber, AffineTransform::identity);
  445. restoreState();
  446. }
  447. }
  448. //==============================================================================
  449. void saveState()
  450. {
  451. (void) getCanvas().callIntMethod (Canvas.save);
  452. stateStack.add (new SavedState (*currentState));
  453. }
  454. void restoreState()
  455. {
  456. SavedState* const top = stateStack.getLast();
  457. if (top != 0)
  458. {
  459. currentState->flattenImageClippingLayer (top->canvas);
  460. currentState = top;
  461. stateStack.removeLast (1, false);
  462. }
  463. else
  464. {
  465. jassertfalse; // trying to pop with an empty stack!
  466. }
  467. getCanvas().callVoidMethod (Canvas.restore);
  468. }
  469. void beginTransparencyLayer (float opacity)
  470. {
  471. Rectangle<int> clip (getClipBounds());
  472. (void) getCanvas().callIntMethod (Canvas.saveLayerAlpha,
  473. (float) clip.getX(),
  474. (float) clip.getY(),
  475. (float) clip.getRight(),
  476. (float) clip.getBottom(),
  477. jlimit (0, 255, roundToInt (opacity * 255.0f)),
  478. 31 /*ALL_SAVE_FLAG*/);
  479. stateStack.add (new SavedState (*currentState));
  480. }
  481. void endTransparencyLayer()
  482. {
  483. restoreState();
  484. }
  485. //==============================================================================
  486. class SavedState
  487. {
  488. public:
  489. SavedState (jobject canvas_)
  490. : canvas (canvas_), font (1.0f), quality (Graphics::highResamplingQuality),
  491. fillNeedsUpdate (true), typefaceNeedsUpdate (true)
  492. {
  493. }
  494. SavedState (const SavedState& other)
  495. : canvas (other.canvas), fillType (other.fillType), font (other.font),
  496. quality (other.quality), fillNeedsUpdate (true), typefaceNeedsUpdate (true)
  497. {
  498. }
  499. void setFillType (const FillType& newType)
  500. {
  501. fillNeedsUpdate = true;
  502. fillType = newType;
  503. }
  504. void setAlpha (float alpha)
  505. {
  506. fillNeedsUpdate = true;
  507. fillType.colour = fillType.colour.withAlpha (alpha);
  508. }
  509. void setInterpolationQuality (Graphics::ResamplingQuality quality_)
  510. {
  511. if (quality != quality_)
  512. {
  513. quality = quality_;
  514. fillNeedsUpdate = true;
  515. paint.clear();
  516. }
  517. }
  518. jobject getPaint()
  519. {
  520. if (fillNeedsUpdate)
  521. {
  522. JNIEnv* env = getEnv();
  523. if (paint.get() == 0)
  524. paint = GlobalRef (GraphicsHelpers::createPaint (quality));
  525. if (fillType.isColour())
  526. {
  527. env->DeleteLocalRef (paint.callObjectMethod (Paint.setShader, (jobject) 0));
  528. paint.callVoidMethod (Paint.setColor, colourToInt (fillType.colour));
  529. }
  530. else if (fillType.isGradient())
  531. {
  532. const ColourGradient& g = *fillType.gradient;
  533. const Point<float> p1 (g.point1);
  534. const Point<float> p2 (g.point2);
  535. const int numColours = g.getNumColours();
  536. jintArray coloursArray = env->NewIntArray (numColours);
  537. jfloatArray positionsArray = env->NewFloatArray (numColours);
  538. {
  539. HeapBlock<int> colours (numColours);
  540. HeapBlock<float> positions (numColours);
  541. for (int i = 0; i < numColours; ++i)
  542. {
  543. colours[i] = colourToInt (g.getColour (i));
  544. positions[i] = (float) g.getColourPosition(i);
  545. }
  546. env->SetIntArrayRegion (coloursArray, 0, numColours, colours.getData());
  547. env->SetFloatArrayRegion (positionsArray, 0, numColours, positions.getData());
  548. }
  549. jobject tileMode = env->GetStaticObjectField (ShaderTileMode, ShaderTileMode.CLAMP);
  550. jobject shader;
  551. if (fillType.gradient->isRadial)
  552. {
  553. shader = env->NewObject (RadialGradientClass,
  554. RadialGradientClass.constructor,
  555. p1.getX(), p1.getY(),
  556. p1.getDistanceFrom (p2),
  557. coloursArray, positionsArray,
  558. tileMode);
  559. }
  560. else
  561. {
  562. shader = env->NewObject (LinearGradientClass,
  563. LinearGradientClass.constructor,
  564. p1.getX(), p1.getY(), p2.getX(), p2.getY(),
  565. coloursArray, positionsArray,
  566. tileMode);
  567. }
  568. env->DeleteLocalRef (tileMode);
  569. env->DeleteLocalRef (coloursArray);
  570. env->DeleteLocalRef (positionsArray);
  571. env->CallVoidMethod (shader, ShaderClass.setLocalMatrix, createMatrixRef (env, fillType.transform).get());
  572. env->DeleteLocalRef (paint.callObjectMethod (Paint.setShader, shader));
  573. env->DeleteLocalRef (shader);
  574. }
  575. else
  576. {
  577. // TODO xxx
  578. }
  579. }
  580. return paint.get();
  581. }
  582. jobject getPaintForTypeface()
  583. {
  584. jobject p = getPaint();
  585. if (typefaceNeedsUpdate)
  586. {
  587. typefaceNeedsUpdate = false;
  588. const Typeface::Ptr t (font.getTypeface());
  589. AndroidTypeface* atf = dynamic_cast <AndroidTypeface*> (t.getObject());
  590. if (atf != 0)
  591. {
  592. paint.callObjectMethod (Paint.setTypeface, atf->typeface.get());
  593. paint.callVoidMethod (Paint.setTextSize, font.getHeight());
  594. const float hScale = font.getHorizontalScale();
  595. if (hScale < 0.99f || hScale > 1.01f)
  596. paint.callVoidMethod (Paint.setTextScaleX, hScale);
  597. }
  598. fillNeedsUpdate = true;
  599. paint.callVoidMethod (Paint.setAlpha, (jint) fillType.colour.getAlpha());
  600. }
  601. return p;
  602. }
  603. jobject getImagePaint()
  604. {
  605. jobject p = getPaint();
  606. paint.callVoidMethod (Paint.setAlpha, (jint) fillType.colour.getAlpha());
  607. fillNeedsUpdate = true;
  608. return p;
  609. }
  610. void flattenImageClippingLayer (jobject previousCanvas)
  611. {
  612. // XXX couldn't get image clipping to work...
  613. if (temporaryLayerBitmap != 0)
  614. {
  615. JNIEnv* env = getEnv();
  616. jobject tileMode = env->GetStaticObjectField (ShaderTileMode, ShaderTileMode.CLAMP);
  617. jobject shader = env->NewObject (BitmapShader, BitmapShader.constructor,
  618. temporaryLayerBitmap.get(), tileMode, tileMode);
  619. env->DeleteLocalRef (tileMode);
  620. jobject compositingPaint = GraphicsHelpers::createPaint (quality);
  621. env->CallObjectMethod (compositingPaint, Paint.setShader, shader);
  622. env->DeleteLocalRef (shader);
  623. LocalRef<jobject> maskBitmap (createAlphaBitmap (env, maskImage));
  624. maskImage = Image::null;
  625. env->CallVoidMethod (previousCanvas, Canvas.drawBitmapAt,
  626. maskBitmap.get(), (jfloat) maskLayerX, (jfloat) maskLayerY, compositingPaint);
  627. env->DeleteLocalRef (compositingPaint);
  628. canvas = GlobalRef (previousCanvas);
  629. env->CallVoidMethod (temporaryLayerBitmap.get(), BitmapClass.recycle);
  630. env->CallVoidMethod (maskBitmap.get(), BitmapClass.recycle);
  631. temporaryLayerBitmap.clear();
  632. }
  633. }
  634. void clipToImage (jobject previousCanvas,
  635. jobject temporaryCanvas, jobject temporaryLayerBitmap_,
  636. const Image& maskImage_,
  637. int maskLayerX_, int maskLayerY_)
  638. {
  639. // XXX couldn't get image clipping to work...
  640. flattenImageClippingLayer (previousCanvas);
  641. maskLayerX = maskLayerX_;
  642. maskLayerY = maskLayerY_;
  643. canvas = GlobalRef (temporaryCanvas);
  644. temporaryLayerBitmap = GlobalRef (temporaryLayerBitmap_);
  645. maskImage = maskImage_;
  646. }
  647. static jobject createAlphaBitmap (JNIEnv* env, const Image& image)
  648. {
  649. Image::BitmapData bm (image, Image::BitmapData::readOnly);
  650. jobject bitmap = AndroidImage::createBitmap (bm.width, bm.height, true);
  651. jintArray intArray = env->NewIntArray (bm.width * bm.height);
  652. jint* const dest = env->GetIntArrayElements (intArray, 0);
  653. for (int yy = 0; yy < bm.height; ++yy)
  654. {
  655. PixelAlpha* src = (PixelAlpha*) bm.getLinePointer (yy);
  656. jint* destLine = dest + yy * bm.width;
  657. for (int xx = 0; xx < bm.width; ++xx)
  658. {
  659. destLine[xx] = src->getAlpha();
  660. src = addBytesToPointer (src, bm.pixelStride);
  661. }
  662. }
  663. env->ReleaseIntArrayElements (intArray, (jint*) dest, 0);
  664. env->CallVoidMethod (bitmap, BitmapClass.setPixels, intArray, 0, bm.width, 0, 0, bm.width, bm.height);
  665. env->DeleteLocalRef (intArray);
  666. return bitmap;
  667. }
  668. GlobalRef canvas, temporaryLayerBitmap;
  669. FillType fillType;
  670. Font font;
  671. GlobalRef paint;
  672. bool fillNeedsUpdate, typefaceNeedsUpdate;
  673. Graphics::ResamplingQuality quality;
  674. Image maskImage;
  675. int maskLayerX, maskLayerY;
  676. };
  677. private:
  678. //==============================================================================
  679. GlobalRef originalCanvas;
  680. ScopedPointer <SavedState> currentState;
  681. OwnedArray <SavedState> stateStack;
  682. GlobalRef& getCanvas() const noexcept { return currentState->canvas; }
  683. jobject getCurrentPaint() const { return currentState->getPaint(); }
  684. jobject getImagePaint() const { return currentState->getImagePaint(); }
  685. static LocalRef<jobject> createPath (JNIEnv* env, const Path& path)
  686. {
  687. jobject p = env->NewObject (PathClass, PathClass.constructor);
  688. Path::Iterator i (path);
  689. while (i.next())
  690. {
  691. switch (i.elementType)
  692. {
  693. case Path::Iterator::startNewSubPath: env->CallVoidMethod (p, PathClass.moveTo, i.x1, i.y1); break;
  694. case Path::Iterator::lineTo: env->CallVoidMethod (p, PathClass.lineTo, i.x1, i.y1); break;
  695. case Path::Iterator::quadraticTo: env->CallVoidMethod (p, PathClass.quadTo, i.x1, i.y1, i.x2, i.y2); break;
  696. case Path::Iterator::cubicTo: env->CallVoidMethod (p, PathClass.cubicTo, i.x1, i.y1, i.x2, i.y2, i.x3, i.y3); break;
  697. case Path::Iterator::closePath: env->CallVoidMethod (p, PathClass.closePath); break;
  698. default: jassertfalse; break;
  699. }
  700. }
  701. return LocalRef<jobject> (p);
  702. }
  703. static LocalRef<jobject> createPath (JNIEnv* env, const Path& path, const AffineTransform& transform)
  704. {
  705. if (transform.isIdentity())
  706. return createPath (env, path);
  707. Path tempPath (path);
  708. tempPath.applyTransform (transform);
  709. return createPath (env, tempPath);
  710. }
  711. static LocalRef<jobject> createMatrixRef (JNIEnv* env, const AffineTransform& t)
  712. {
  713. return LocalRef<jobject> (GraphicsHelpers::createMatrix (env, t));
  714. }
  715. static LocalRef<jobject> createRect (JNIEnv* env, const Rectangle<int>& r)
  716. {
  717. return LocalRef<jobject> (env->NewObject (RectClass, RectClass.constructor,
  718. r.getX(), r.getY(), r.getRight(), r.getBottom()));
  719. }
  720. static LocalRef<jobject> createRegion (JNIEnv* env, const RectangleList& list)
  721. {
  722. jobject region = env->NewObject (RegionClass, RegionClass.constructor);
  723. const int numRects = list.getNumRectangles();
  724. for (int i = 0; i < numRects; ++i)
  725. env->CallBooleanMethod (region, RegionClass.regionUnion, createRect (env, list.getRectangle(i)).get());
  726. return LocalRef<jobject> (region);
  727. }
  728. static int colourToInt (const Colour& col) noexcept
  729. {
  730. return col.getARGB();
  731. }
  732. template <class PixelType>
  733. static void copyPixels (jint* const dest, const PixelType* src, const int width, const int pixelStride) noexcept
  734. {
  735. for (int x = 0; x < width; ++x)
  736. {
  737. dest[x] = src->getUnpremultipliedARGB();
  738. src = addBytesToPointer (src, pixelStride);
  739. }
  740. }
  741. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidLowLevelGraphicsContext);
  742. };
  743. LowLevelGraphicsContext* AndroidImage::createLowLevelContext()
  744. {
  745. jobject canvas = getEnv()->NewObject (Canvas, Canvas.constructor, bitmap.get());
  746. return new AndroidLowLevelGraphicsContext (canvas);
  747. }
  748. #endif