123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- /*
- ==============================================================================
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
- JUCE is an open source library subject to commercial or open-source
- licensing.
- By using JUCE, you agree to the terms of both the JUCE 5 End-User License
- Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
- 27th April 2017).
- End User License Agreement: www.juce.com/juce-5-licence
- Privacy Policy: www.juce.com/juce-5-privacy-policy
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
- ==============================================================================
- */
- namespace juce
- {
- // this will throw an assertion if you try to draw something that's not
- // possible in postscript
- #define WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS 0
- //==============================================================================
- #if JUCE_DEBUG && WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS
- #define notPossibleInPostscriptAssert jassertfalse
- #else
- #define notPossibleInPostscriptAssert
- #endif
- //==============================================================================
- LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript,
- const String& documentTitle,
- const int totalWidth_,
- const int totalHeight_)
- : out (resultingPostScript),
- totalWidth (totalWidth_),
- totalHeight (totalHeight_),
- needToClip (true)
- {
- stateStack.add (new SavedState());
- stateStack.getLast()->clip = Rectangle<int> (totalWidth_, totalHeight_);
- const float scale = jmin ((520.0f / totalWidth_), (750.0f / totalHeight));
- out << "%!PS-Adobe-3.0 EPSF-3.0"
- "\n%%BoundingBox: 0 0 600 824"
- "\n%%Pages: 0"
- "\n%%Creator: ROLI Ltd. JUCE"
- "\n%%Title: " << documentTitle <<
- "\n%%CreationDate: none"
- "\n%%LanguageLevel: 2"
- "\n%%EndComments"
- "\n%%BeginProlog"
- "\n%%BeginResource: JRes"
- "\n/bd {bind def} bind def"
- "\n/c {setrgbcolor} bd"
- "\n/m {moveto} bd"
- "\n/l {lineto} bd"
- "\n/rl {rlineto} bd"
- "\n/ct {curveto} bd"
- "\n/cp {closepath} bd"
- "\n/pr {3 index 3 index moveto 1 index 0 rlineto 0 1 index rlineto pop neg 0 rlineto pop pop closepath} bd"
- "\n/doclip {initclip newpath} bd"
- "\n/endclip {clip newpath} bd"
- "\n%%EndResource"
- "\n%%EndProlog"
- "\n%%BeginSetup"
- "\n%%EndSetup"
- "\n%%Page: 1 1"
- "\n%%BeginPageSetup"
- "\n%%EndPageSetup\n\n"
- << "40 800 translate\n"
- << scale << ' ' << scale << " scale\n\n";
- }
- LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer()
- {
- }
- //==============================================================================
- bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const
- {
- return true;
- }
- void LowLevelGraphicsPostScriptRenderer::setOrigin (Point<int> o)
- {
- if (! o.isOrigin())
- {
- stateStack.getLast()->xOffset += o.x;
- stateStack.getLast()->yOffset += o.y;
- needToClip = true;
- }
- }
- void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /*transform*/)
- {
- //xxx
- jassertfalse;
- }
- float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() { return 1.0f; }
- bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle<int>& r)
- {
- needToClip = true;
- return stateStack.getLast()->clip.clipTo (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
- }
- bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList<int>& clipRegion)
- {
- needToClip = true;
- return stateStack.getLast()->clip.clipTo (clipRegion);
- }
- void LowLevelGraphicsPostScriptRenderer::excludeClipRectangle (const Rectangle<int>& r)
- {
- needToClip = true;
- stateStack.getLast()->clip.subtract (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
- }
- void LowLevelGraphicsPostScriptRenderer::clipToPath (const Path& path, const AffineTransform& transform)
- {
- writeClip();
- Path p (path);
- p.applyTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
- writePath (p);
- out << "clip\n";
- }
- void LowLevelGraphicsPostScriptRenderer::clipToImageAlpha (const Image& /*sourceImage*/, const AffineTransform& /*transform*/)
- {
- needToClip = true;
- jassertfalse; // xxx
- }
- bool LowLevelGraphicsPostScriptRenderer::clipRegionIntersects (const Rectangle<int>& r)
- {
- return stateStack.getLast()->clip.intersectsRectangle (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
- }
- Rectangle<int> LowLevelGraphicsPostScriptRenderer::getClipBounds() const
- {
- return stateStack.getLast()->clip.getBounds().translated (-stateStack.getLast()->xOffset,
- -stateStack.getLast()->yOffset);
- }
- bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const
- {
- return stateStack.getLast()->clip.isEmpty();
- }
- //==============================================================================
- LowLevelGraphicsPostScriptRenderer::SavedState::SavedState()
- : xOffset (0),
- yOffset (0)
- {
- }
- LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState()
- {
- }
- void LowLevelGraphicsPostScriptRenderer::saveState()
- {
- stateStack.add (new SavedState (*stateStack.getLast()));
- }
- void LowLevelGraphicsPostScriptRenderer::restoreState()
- {
- jassert (stateStack.size() > 0);
- if (stateStack.size() > 0)
- stateStack.removeLast();
- }
- void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float)
- {
- }
- void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer()
- {
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::writeClip()
- {
- if (needToClip)
- {
- needToClip = false;
- out << "doclip ";
- int itemsOnLine = 0;
- for (auto& i : stateStack.getLast()->clip)
- {
- if (++itemsOnLine == 6)
- {
- itemsOnLine = 0;
- out << '\n';
- }
- out << i.getX() << ' ' << -i.getY() << ' '
- << i.getWidth() << ' ' << -i.getHeight() << " pr ";
- }
- out << "endclip\n";
- }
- }
- void LowLevelGraphicsPostScriptRenderer::writeColour (Colour colour)
- {
- Colour c (Colours::white.overlaidWith (colour));
- if (lastColour != c)
- {
- lastColour = c;
- out << String (c.getFloatRed(), 3) << ' '
- << String (c.getFloatGreen(), 3) << ' '
- << String (c.getFloatBlue(), 3) << " c\n";
- }
- }
- void LowLevelGraphicsPostScriptRenderer::writeXY (const float x, const float y) const
- {
- out << String (x, 2) << ' '
- << String (-y, 2) << ' ';
- }
- void LowLevelGraphicsPostScriptRenderer::writePath (const Path& path) const
- {
- out << "newpath ";
- float lastX = 0.0f;
- float lastY = 0.0f;
- int itemsOnLine = 0;
- Path::Iterator i (path);
- while (i.next())
- {
- if (++itemsOnLine == 4)
- {
- itemsOnLine = 0;
- out << '\n';
- }
- switch (i.elementType)
- {
- case Path::Iterator::startNewSubPath:
- writeXY (i.x1, i.y1);
- lastX = i.x1;
- lastY = i.y1;
- out << "m ";
- break;
- case Path::Iterator::lineTo:
- writeXY (i.x1, i.y1);
- lastX = i.x1;
- lastY = i.y1;
- out << "l ";
- break;
- case Path::Iterator::quadraticTo:
- {
- const float cp1x = lastX + (i.x1 - lastX) * 2.0f / 3.0f;
- const float cp1y = lastY + (i.y1 - lastY) * 2.0f / 3.0f;
- const float cp2x = cp1x + (i.x2 - lastX) / 3.0f;
- const float cp2y = cp1y + (i.y2 - lastY) / 3.0f;
- writeXY (cp1x, cp1y);
- writeXY (cp2x, cp2y);
- writeXY (i.x2, i.y2);
- out << "ct ";
- lastX = i.x2;
- lastY = i.y2;
- }
- break;
- case Path::Iterator::cubicTo:
- writeXY (i.x1, i.y1);
- writeXY (i.x2, i.y2);
- writeXY (i.x3, i.y3);
- out << "ct ";
- lastX = i.x3;
- lastY = i.y3;
- break;
- case Path::Iterator::closePath:
- out << "cp ";
- break;
- default:
- jassertfalse;
- break;
- }
- }
- out << '\n';
- }
- void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& trans) const
- {
- out << "[ "
- << trans.mat00 << ' '
- << trans.mat10 << ' '
- << trans.mat01 << ' '
- << trans.mat11 << ' '
- << trans.mat02 << ' '
- << trans.mat12 << " ] concat ";
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::setFill (const FillType& fillType)
- {
- stateStack.getLast()->fillType = fillType;
- }
- void LowLevelGraphicsPostScriptRenderer::setOpacity (float /*opacity*/)
- {
- }
- void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
- {
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<int>& r, const bool /*replaceExistingContents*/)
- {
- fillRect (r.toFloat());
- }
- void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<float>& r)
- {
- if (stateStack.getLast()->fillType.isColour())
- {
- writeClip();
- writeColour (stateStack.getLast()->fillType.colour);
- auto r2 = r.translated ((float) stateStack.getLast()->xOffset,
- (float) stateStack.getLast()->yOffset);
- out << r2.getX() << ' ' << -r2.getBottom() << ' ' << r2.getWidth() << ' ' << r2.getHeight() << " rectfill\n";
- }
- else
- {
- Path p;
- p.addRectangle (r);
- fillPath (p, AffineTransform());
- }
- }
- void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList<float>& list)
- {
- fillPath (list.toPath(), AffineTransform());
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t)
- {
- if (stateStack.getLast()->fillType.isColour())
- {
- writeClip();
- Path p (path);
- p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset,
- (float) stateStack.getLast()->yOffset));
- writePath (p);
- writeColour (stateStack.getLast()->fillType.colour);
- out << "fill\n";
- }
- else if (stateStack.getLast()->fillType.isGradient())
- {
- // this doesn't work correctly yet - it could be improved to handle solid gradients, but
- // postscript can't do semi-transparent ones.
- notPossibleInPostscriptAssert; // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file
- writeClip();
- out << "gsave ";
- {
- Path p (path);
- p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
- writePath (p);
- out << "clip\n";
- }
- auto bounds = stateStack.getLast()->clip.getBounds();
- // ideally this would draw lots of lines or ellipses to approximate the gradient, but for the
- // time-being, this just fills it with the average colour..
- writeColour (stateStack.getLast()->fillType.gradient->getColourAtPosition (0.5f));
- out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n";
- out << "grestore\n";
- }
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im,
- const int sx, const int sy,
- const int maxW, const int maxH) const
- {
- out << "{<\n";
- const int w = jmin (maxW, im.getWidth());
- const int h = jmin (maxH, im.getHeight());
- int charsOnLine = 0;
- const Image::BitmapData srcData (im, 0, 0, w, h);
- Colour pixel;
- for (int y = h; --y >= 0;)
- {
- for (int x = 0; x < w; ++x)
- {
- const uint8* pixelData = srcData.getPixelPointer (x, y);
- if (x >= sx && y >= sy)
- {
- if (im.isARGB())
- {
- PixelARGB p (*(const PixelARGB*) pixelData);
- p.unpremultiply();
- pixel = Colours::white.overlaidWith (Colour (p));
- }
- else if (im.isRGB())
- {
- pixel = Colour (*((const PixelRGB*) pixelData));
- }
- else
- {
- pixel = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixelData);
- }
- }
- else
- {
- pixel = Colours::transparentWhite;
- }
- const uint8 pixelValues[3] = { pixel.getRed(), pixel.getGreen(), pixel.getBlue() };
- out << String::toHexString (pixelValues, 3, 0);
- charsOnLine += 3;
- if (charsOnLine > 100)
- {
- out << '\n';
- charsOnLine = 0;
- }
- }
- }
- out << "\n>}\n";
- }
- void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform)
- {
- const int w = sourceImage.getWidth();
- const int h = sourceImage.getHeight();
- writeClip();
- out << "gsave ";
- writeTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)
- .scaled (1.0f, -1.0f));
- RectangleList<int> imageClip;
- sourceImage.createSolidAreaMask (imageClip, 0.5f);
- out << "newpath ";
- int itemsOnLine = 0;
- for (auto& i : imageClip)
- {
- if (++itemsOnLine == 6)
- {
- out << '\n';
- itemsOnLine = 0;
- }
- out << i.getX() << ' ' << i.getY() << ' ' << i.getWidth() << ' ' << i.getHeight() << " pr ";
- }
- out << " clip newpath\n";
- out << w << ' ' << h << " scale\n";
- out << w << ' ' << h << " 8 [" << w << " 0 0 -" << h << ' ' << (int) 0 << ' ' << h << " ]\n";
- writeImage (sourceImage, 0, 0, w, h);
- out << "false 3 colorimage grestore\n";
- needToClip = true;
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::drawLine (const Line <float>& line)
- {
- Path p;
- p.addLineSegment (line, 1.0f);
- fillPath (p, AffineTransform());
- }
- //==============================================================================
- void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont)
- {
- stateStack.getLast()->font = newFont;
- }
- const Font& LowLevelGraphicsPostScriptRenderer::getFont()
- {
- return stateStack.getLast()->font;
- }
- void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
- {
- Path p;
- Font& font = stateStack.getLast()->font;
- font.getTypeface()->getOutlineForGlyph (glyphNumber, p);
- fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform));
- }
- } // namespace juce
|