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.

547 lines
17KB

  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. BEGIN_JUCE_NAMESPACE
  19. // this will throw an assertion if you try to draw something that's not
  20. // possible in postscript
  21. #define WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS 0
  22. //==============================================================================
  23. #if JUCE_DEBUG && WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS
  24. #define notPossibleInPostscriptAssert jassertfalse
  25. #else
  26. #define notPossibleInPostscriptAssert
  27. #endif
  28. //==============================================================================
  29. LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript,
  30. const String& documentTitle,
  31. const int totalWidth_,
  32. const int totalHeight_)
  33. : out (resultingPostScript),
  34. totalWidth (totalWidth_),
  35. totalHeight (totalHeight_),
  36. needToClip (true)
  37. {
  38. stateStack.add (new SavedState());
  39. stateStack.getLast()->clip = Rectangle<int> (totalWidth_, totalHeight_);
  40. const float scale = jmin ((520.0f / totalWidth_), (750.0f / totalHeight));
  41. out << "%!PS-Adobe-3.0 EPSF-3.0"
  42. "\n%%BoundingBox: 0 0 600 824"
  43. "\n%%Pages: 0"
  44. "\n%%Creator: Raw Material Software JUCE"
  45. "\n%%Title: " << documentTitle <<
  46. "\n%%CreationDate: none"
  47. "\n%%LanguageLevel: 2"
  48. "\n%%EndComments"
  49. "\n%%BeginProlog"
  50. "\n%%BeginResource: JRes"
  51. "\n/bd {bind def} bind def"
  52. "\n/c {setrgbcolor} bd"
  53. "\n/m {moveto} bd"
  54. "\n/l {lineto} bd"
  55. "\n/rl {rlineto} bd"
  56. "\n/ct {curveto} bd"
  57. "\n/cp {closepath} bd"
  58. "\n/pr {3 index 3 index moveto 1 index 0 rlineto 0 1 index rlineto pop neg 0 rlineto pop pop closepath} bd"
  59. "\n/doclip {initclip newpath} bd"
  60. "\n/endclip {clip newpath} bd"
  61. "\n%%EndResource"
  62. "\n%%EndProlog"
  63. "\n%%BeginSetup"
  64. "\n%%EndSetup"
  65. "\n%%Page: 1 1"
  66. "\n%%BeginPageSetup"
  67. "\n%%EndPageSetup\n\n"
  68. << "40 800 translate\n"
  69. << scale << ' ' << scale << " scale\n\n";
  70. }
  71. LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer()
  72. {
  73. }
  74. //==============================================================================
  75. bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const
  76. {
  77. return true;
  78. }
  79. void LowLevelGraphicsPostScriptRenderer::setOrigin (int x, int y)
  80. {
  81. if (x != 0 || y != 0)
  82. {
  83. stateStack.getLast()->xOffset += x;
  84. stateStack.getLast()->yOffset += y;
  85. needToClip = true;
  86. }
  87. }
  88. void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /*transform*/)
  89. {
  90. //xxx
  91. jassertfalse;
  92. }
  93. float LowLevelGraphicsPostScriptRenderer::getScaleFactor()
  94. {
  95. jassertfalse; //xxx
  96. return 1.0f;
  97. }
  98. bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle<int>& r)
  99. {
  100. needToClip = true;
  101. return stateStack.getLast()->clip.clipTo (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
  102. }
  103. bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList& clipRegion)
  104. {
  105. needToClip = true;
  106. return stateStack.getLast()->clip.clipTo (clipRegion);
  107. }
  108. void LowLevelGraphicsPostScriptRenderer::excludeClipRectangle (const Rectangle<int>& r)
  109. {
  110. needToClip = true;
  111. stateStack.getLast()->clip.subtract (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
  112. }
  113. void LowLevelGraphicsPostScriptRenderer::clipToPath (const Path& path, const AffineTransform& transform)
  114. {
  115. writeClip();
  116. Path p (path);
  117. p.applyTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
  118. writePath (p);
  119. out << "clip\n";
  120. }
  121. void LowLevelGraphicsPostScriptRenderer::clipToImageAlpha (const Image& /*sourceImage*/, const AffineTransform& /*transform*/)
  122. {
  123. needToClip = true;
  124. jassertfalse; // xxx
  125. }
  126. bool LowLevelGraphicsPostScriptRenderer::clipRegionIntersects (const Rectangle<int>& r)
  127. {
  128. return stateStack.getLast()->clip.intersectsRectangle (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
  129. }
  130. Rectangle<int> LowLevelGraphicsPostScriptRenderer::getClipBounds() const
  131. {
  132. return stateStack.getLast()->clip.getBounds().translated (-stateStack.getLast()->xOffset,
  133. -stateStack.getLast()->yOffset);
  134. }
  135. bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const
  136. {
  137. return stateStack.getLast()->clip.isEmpty();
  138. }
  139. //==============================================================================
  140. LowLevelGraphicsPostScriptRenderer::SavedState::SavedState()
  141. : xOffset (0),
  142. yOffset (0)
  143. {
  144. }
  145. LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState()
  146. {
  147. }
  148. void LowLevelGraphicsPostScriptRenderer::saveState()
  149. {
  150. stateStack.add (new SavedState (*stateStack.getLast()));
  151. }
  152. void LowLevelGraphicsPostScriptRenderer::restoreState()
  153. {
  154. jassert (stateStack.size() > 0);
  155. if (stateStack.size() > 0)
  156. stateStack.removeLast();
  157. }
  158. void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float)
  159. {
  160. }
  161. void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer()
  162. {
  163. }
  164. //==============================================================================
  165. void LowLevelGraphicsPostScriptRenderer::writeClip()
  166. {
  167. if (needToClip)
  168. {
  169. needToClip = false;
  170. out << "doclip ";
  171. int itemsOnLine = 0;
  172. for (RectangleList::Iterator i (stateStack.getLast()->clip); i.next();)
  173. {
  174. if (++itemsOnLine == 6)
  175. {
  176. itemsOnLine = 0;
  177. out << '\n';
  178. }
  179. const Rectangle<int>& r = *i.getRectangle();
  180. out << r.getX() << ' ' << -r.getY() << ' '
  181. << r.getWidth() << ' ' << -r.getHeight() << " pr ";
  182. }
  183. out << "endclip\n";
  184. }
  185. }
  186. void LowLevelGraphicsPostScriptRenderer::writeColour (const Colour& colour)
  187. {
  188. Colour c (Colours::white.overlaidWith (colour));
  189. if (lastColour != c)
  190. {
  191. lastColour = c;
  192. out << String (c.getFloatRed(), 3) << ' '
  193. << String (c.getFloatGreen(), 3) << ' '
  194. << String (c.getFloatBlue(), 3) << " c\n";
  195. }
  196. }
  197. void LowLevelGraphicsPostScriptRenderer::writeXY (const float x, const float y) const
  198. {
  199. out << String (x, 2) << ' '
  200. << String (-y, 2) << ' ';
  201. }
  202. void LowLevelGraphicsPostScriptRenderer::writePath (const Path& path) const
  203. {
  204. out << "newpath ";
  205. float lastX = 0.0f;
  206. float lastY = 0.0f;
  207. int itemsOnLine = 0;
  208. Path::Iterator i (path);
  209. while (i.next())
  210. {
  211. if (++itemsOnLine == 4)
  212. {
  213. itemsOnLine = 0;
  214. out << '\n';
  215. }
  216. switch (i.elementType)
  217. {
  218. case Path::Iterator::startNewSubPath:
  219. writeXY (i.x1, i.y1);
  220. lastX = i.x1;
  221. lastY = i.y1;
  222. out << "m ";
  223. break;
  224. case Path::Iterator::lineTo:
  225. writeXY (i.x1, i.y1);
  226. lastX = i.x1;
  227. lastY = i.y1;
  228. out << "l ";
  229. break;
  230. case Path::Iterator::quadraticTo:
  231. {
  232. const float cp1x = lastX + (i.x1 - lastX) * 2.0f / 3.0f;
  233. const float cp1y = lastY + (i.y1 - lastY) * 2.0f / 3.0f;
  234. const float cp2x = cp1x + (i.x2 - lastX) / 3.0f;
  235. const float cp2y = cp1y + (i.y2 - lastY) / 3.0f;
  236. writeXY (cp1x, cp1y);
  237. writeXY (cp2x, cp2y);
  238. writeXY (i.x2, i.y2);
  239. out << "ct ";
  240. lastX = i.x2;
  241. lastY = i.y2;
  242. }
  243. break;
  244. case Path::Iterator::cubicTo:
  245. writeXY (i.x1, i.y1);
  246. writeXY (i.x2, i.y2);
  247. writeXY (i.x3, i.y3);
  248. out << "ct ";
  249. lastX = i.x3;
  250. lastY = i.y3;
  251. break;
  252. case Path::Iterator::closePath:
  253. out << "cp ";
  254. break;
  255. default:
  256. jassertfalse;
  257. break;
  258. }
  259. }
  260. out << '\n';
  261. }
  262. void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& trans) const
  263. {
  264. out << "[ "
  265. << trans.mat00 << ' '
  266. << trans.mat10 << ' '
  267. << trans.mat01 << ' '
  268. << trans.mat11 << ' '
  269. << trans.mat02 << ' '
  270. << trans.mat12 << " ] concat ";
  271. }
  272. //==============================================================================
  273. void LowLevelGraphicsPostScriptRenderer::setFill (const FillType& fillType)
  274. {
  275. stateStack.getLast()->fillType = fillType;
  276. }
  277. void LowLevelGraphicsPostScriptRenderer::setOpacity (float /*opacity*/)
  278. {
  279. }
  280. void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
  281. {
  282. }
  283. //==============================================================================
  284. void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<int>& r, const bool /*replaceExistingContents*/)
  285. {
  286. if (stateStack.getLast()->fillType.isColour())
  287. {
  288. writeClip();
  289. writeColour (stateStack.getLast()->fillType.colour);
  290. Rectangle<int> r2 (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
  291. out << r2.getX() << ' ' << -r2.getBottom() << ' ' << r2.getWidth() << ' ' << r2.getHeight() << " rectfill\n";
  292. }
  293. else
  294. {
  295. Path p;
  296. p.addRectangle (r);
  297. fillPath (p, AffineTransform::identity);
  298. }
  299. }
  300. //==============================================================================
  301. void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t)
  302. {
  303. if (stateStack.getLast()->fillType.isColour())
  304. {
  305. writeClip();
  306. Path p (path);
  307. p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset,
  308. (float) stateStack.getLast()->yOffset));
  309. writePath (p);
  310. writeColour (stateStack.getLast()->fillType.colour);
  311. out << "fill\n";
  312. }
  313. else if (stateStack.getLast()->fillType.isGradient())
  314. {
  315. // this doesn't work correctly yet - it could be improved to handle solid gradients, but
  316. // postscript can't do semi-transparent ones.
  317. notPossibleInPostscriptAssert // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file
  318. writeClip();
  319. out << "gsave ";
  320. {
  321. Path p (path);
  322. p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
  323. writePath (p);
  324. out << "clip\n";
  325. }
  326. const Rectangle<int> bounds (stateStack.getLast()->clip.getBounds());
  327. // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the
  328. // time-being, this just fills it with the average colour..
  329. writeColour (stateStack.getLast()->fillType.gradient->getColourAtPosition (0.5f));
  330. out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n";
  331. out << "grestore\n";
  332. }
  333. }
  334. //==============================================================================
  335. void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im,
  336. const int sx, const int sy,
  337. const int maxW, const int maxH) const
  338. {
  339. out << "{<\n";
  340. const int w = jmin (maxW, im.getWidth());
  341. const int h = jmin (maxH, im.getHeight());
  342. int charsOnLine = 0;
  343. const Image::BitmapData srcData (im, 0, 0, w, h);
  344. Colour pixel;
  345. for (int y = h; --y >= 0;)
  346. {
  347. for (int x = 0; x < w; ++x)
  348. {
  349. const uint8* pixelData = srcData.getPixelPointer (x, y);
  350. if (x >= sx && y >= sy)
  351. {
  352. if (im.isARGB())
  353. {
  354. PixelARGB p (*(const PixelARGB*) pixelData);
  355. p.unpremultiply();
  356. pixel = Colours::white.overlaidWith (Colour (p.getARGB()));
  357. }
  358. else if (im.isRGB())
  359. {
  360. pixel = Colour (((const PixelRGB*) pixelData)->getARGB());
  361. }
  362. else
  363. {
  364. pixel = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixelData);
  365. }
  366. }
  367. else
  368. {
  369. pixel = Colours::transparentWhite;
  370. }
  371. const uint8 pixelValues[3] = { pixel.getRed(), pixel.getGreen(), pixel.getBlue() };
  372. out << String::toHexString (pixelValues, 3, 0);
  373. charsOnLine += 3;
  374. if (charsOnLine > 100)
  375. {
  376. out << '\n';
  377. charsOnLine = 0;
  378. }
  379. }
  380. }
  381. out << "\n>}\n";
  382. }
  383. void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform, const bool /*fillEntireClipAsTiles*/)
  384. {
  385. const int w = sourceImage.getWidth();
  386. const int h = sourceImage.getHeight();
  387. writeClip();
  388. out << "gsave ";
  389. writeTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)
  390. .scaled (1.0f, -1.0f));
  391. RectangleList imageClip;
  392. sourceImage.createSolidAreaMask (imageClip, 0.5f);
  393. out << "newpath ";
  394. int itemsOnLine = 0;
  395. for (RectangleList::Iterator i (imageClip); i.next();)
  396. {
  397. if (++itemsOnLine == 6)
  398. {
  399. out << '\n';
  400. itemsOnLine = 0;
  401. }
  402. const Rectangle<int>& r = *i.getRectangle();
  403. out << r.getX() << ' ' << r.getY() << ' ' << r.getWidth() << ' ' << r.getHeight() << " pr ";
  404. }
  405. out << " clip newpath\n";
  406. out << w << ' ' << h << " scale\n";
  407. out << w << ' ' << h << " 8 [" << w << " 0 0 -" << h << ' ' << (int) 0 << ' ' << h << " ]\n";
  408. writeImage (sourceImage, 0, 0, w, h);
  409. out << "false 3 colorimage grestore\n";
  410. needToClip = true;
  411. }
  412. //==============================================================================
  413. void LowLevelGraphicsPostScriptRenderer::drawLine (const Line <float>& line)
  414. {
  415. Path p;
  416. p.addLineSegment (line, 1.0f);
  417. fillPath (p, AffineTransform::identity);
  418. }
  419. void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, float top, float bottom)
  420. {
  421. drawLine (Line<float> ((float) x, top, (float) x, bottom));
  422. }
  423. void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, float left, float right)
  424. {
  425. drawLine (Line<float> (left, (float) y, right, (float) y));
  426. }
  427. //==============================================================================
  428. void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont)
  429. {
  430. stateStack.getLast()->font = newFont;
  431. }
  432. Font LowLevelGraphicsPostScriptRenderer::getFont()
  433. {
  434. return stateStack.getLast()->font;
  435. }
  436. void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
  437. {
  438. Path p;
  439. Font& font = stateStack.getLast()->font;
  440. font.getTypeface()->getOutlineForGlyph (glyphNumber, p);
  441. fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform));
  442. }
  443. END_JUCE_NAMESPACE