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.

594 lines
19KB

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