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.

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