michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_SVG_GLYPHS_WRAPPER_H michael@0: #define GFX_SVG_GLYPHS_WRAPPER_H michael@0: michael@0: #include "gfxFontUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsBaseHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "gfxPattern.h" michael@0: #include "mozilla/gfx/UserData.h" michael@0: #include "nsRefreshDriver.h" michael@0: #include "DrawMode.h" michael@0: michael@0: class nsIDocument; michael@0: class nsIContentViewer; michael@0: class nsIPresShell; michael@0: class gfxSVGGlyphs; michael@0: class gfxTextContextPaint; michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class Element; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Wraps an SVG document contained in the SVG table of an OpenType font. michael@0: * There may be multiple SVG documents in an SVG table which we lazily parse michael@0: * so we have an instance of this class for every document in the SVG table michael@0: * which contains a glyph ID which has been used michael@0: * Finds and looks up elements contained in the SVG document which have glyph michael@0: * mappings to be drawn by gfxSVGGlyphs michael@0: */ michael@0: class gfxSVGGlyphsDocument MOZ_FINAL : public nsAPostRefreshObserver michael@0: { michael@0: typedef mozilla::dom::Element Element; michael@0: michael@0: public: michael@0: gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen, michael@0: gfxSVGGlyphs *aSVGGlyphs); michael@0: michael@0: Element *GetGlyphElement(uint32_t aGlyphId); michael@0: michael@0: ~gfxSVGGlyphsDocument(); michael@0: michael@0: virtual void DidRefresh() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsresult ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen); michael@0: michael@0: nsresult SetupPresentation(); michael@0: michael@0: void FindGlyphElements(Element *aElement); michael@0: michael@0: void InsertGlyphId(Element *aGlyphElement); michael@0: michael@0: // Weak so as not to create a cycle. mOwner owns us so this can't dangle. michael@0: gfxSVGGlyphs* mOwner; michael@0: nsCOMPtr mDocument; michael@0: nsCOMPtr mViewer; michael@0: nsCOMPtr mPresShell; michael@0: michael@0: nsBaseHashtable mGlyphIdMap; michael@0: michael@0: nsAutoCString mSVGGlyphsDocumentURI; michael@0: }; michael@0: michael@0: /** michael@0: * Used by |gfxFontEntry| to represent the SVG table of an OpenType font. michael@0: * Handles lazy parsing of the SVG documents in the table, looking up SVG glyphs michael@0: * and rendering SVG glyphs. michael@0: * Each |gfxFontEntry| owns at most one |gfxSVGGlyphs| instance. michael@0: */ michael@0: class gfxSVGGlyphs michael@0: { michael@0: private: michael@0: typedef mozilla::dom::Element Element; michael@0: michael@0: public: michael@0: /** michael@0: * @param aSVGTable The SVG table from the OpenType font michael@0: * michael@0: * The gfxSVGGlyphs object takes over ownership of the blob references michael@0: * that are passed in, and will hb_blob_destroy() them when finished; michael@0: * the caller should -not- destroy these references. michael@0: */ michael@0: gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry); michael@0: michael@0: /** michael@0: * Releases our references to the SVG table and cleans up everything else. michael@0: */ michael@0: ~gfxSVGGlyphs(); michael@0: michael@0: /** michael@0: * This is called when the refresh driver has ticked. michael@0: */ michael@0: void DidRefresh(); michael@0: michael@0: /** michael@0: * Find the |gfxSVGGlyphsDocument| containing an SVG glyph for |aGlyphId|. michael@0: * If |aGlyphId| does not map to an SVG document, return null. michael@0: * If a |gfxSVGGlyphsDocument| has not been created for the document, create one. michael@0: */ michael@0: gfxSVGGlyphsDocument *FindOrCreateGlyphsDocument(uint32_t aGlyphId); michael@0: michael@0: /** michael@0: * Return true iff there is an SVG glyph for |aGlyphId| michael@0: */ michael@0: bool HasSVGGlyph(uint32_t aGlyphId); michael@0: michael@0: /** michael@0: * Render the SVG glyph for |aGlyphId| michael@0: * @param aDrawMode Whether to fill or stroke or both; see DrawMode michael@0: * @param aContextPaint Information on text context paints. michael@0: * See |gfxTextContextPaint|. michael@0: */ michael@0: bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode, michael@0: gfxTextContextPaint *aContextPaint); michael@0: michael@0: /** michael@0: * Get the extents for the SVG glyph associated with |aGlyphId| michael@0: * @param aSVGToAppSpace The matrix mapping the SVG glyph space to the michael@0: * target context space michael@0: */ michael@0: bool GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace, michael@0: gfxRect *aResult); michael@0: michael@0: private: michael@0: Element *GetGlyphElement(uint32_t aGlyphId); michael@0: michael@0: nsClassHashtable mGlyphDocs; michael@0: nsBaseHashtable mGlyphIdMap; michael@0: michael@0: hb_blob_t *mSVGData; michael@0: gfxFontEntry *mFontEntry; michael@0: michael@0: const struct Header { michael@0: mozilla::AutoSwap_PRUint16 mVersion; michael@0: mozilla::AutoSwap_PRUint32 mDocIndexOffset; michael@0: mozilla::AutoSwap_PRUint32 mColorPalettesOffset; michael@0: } *mHeader; michael@0: michael@0: struct IndexEntry { michael@0: mozilla::AutoSwap_PRUint16 mStartGlyph; michael@0: mozilla::AutoSwap_PRUint16 mEndGlyph; michael@0: mozilla::AutoSwap_PRUint32 mDocOffset; michael@0: mozilla::AutoSwap_PRUint32 mDocLength; michael@0: }; michael@0: michael@0: const struct DocIndex { michael@0: mozilla::AutoSwap_PRUint16 mNumEntries; michael@0: IndexEntry mEntries[1]; /* actual length = mNumEntries */ michael@0: } *mDocIndex; michael@0: michael@0: static int CompareIndexEntries(const void *_a, const void *_b); michael@0: }; michael@0: michael@0: /** michael@0: * Used for trickling down paint information through to SVG glyphs. michael@0: */ michael@0: class gfxTextContextPaint michael@0: { michael@0: protected: michael@0: gfxTextContextPaint() { } michael@0: michael@0: public: michael@0: static mozilla::gfx::UserDataKey sUserDataKey; michael@0: michael@0: /* michael@0: * Get text context pattern with the specified opacity value. michael@0: * This lets us inherit paints and paint opacities (i.e. fill/stroke and michael@0: * fill-opacity/stroke-opacity) separately. michael@0: */ michael@0: virtual already_AddRefed GetFillPattern(float aOpacity, michael@0: const gfxMatrix& aCTM) = 0; michael@0: virtual already_AddRefed GetStrokePattern(float aOpacity, michael@0: const gfxMatrix& aCTM) = 0; michael@0: michael@0: virtual float GetFillOpacity() { return 1.0f; } michael@0: virtual float GetStrokeOpacity() { return 1.0f; } michael@0: michael@0: void InitStrokeGeometry(gfxContext *aContext, michael@0: float devUnitsPerSVGUnit); michael@0: michael@0: FallibleTArray& GetStrokeDashArray() { michael@0: return mDashes; michael@0: } michael@0: michael@0: gfxFloat GetStrokeDashOffset() { michael@0: return mDashOffset; michael@0: } michael@0: michael@0: gfxFloat GetStrokeWidth() { michael@0: return mStrokeWidth; michael@0: } michael@0: michael@0: already_AddRefed GetFillPattern(const gfxMatrix& aCTM) { michael@0: return GetFillPattern(GetFillOpacity(), aCTM); michael@0: } michael@0: michael@0: already_AddRefed GetStrokePattern(const gfxMatrix& aCTM) { michael@0: return GetStrokePattern(GetStrokeOpacity(), aCTM); michael@0: } michael@0: michael@0: virtual ~gfxTextContextPaint() { } michael@0: michael@0: private: michael@0: FallibleTArray mDashes; michael@0: gfxFloat mDashOffset; michael@0: gfxFloat mStrokeWidth; michael@0: }; michael@0: michael@0: /** michael@0: * For passing in patterns where the text context has no separate pattern michael@0: * opacity value. michael@0: */ michael@0: class SimpleTextContextPaint : public gfxTextContextPaint michael@0: { michael@0: private: michael@0: static const gfxRGBA sZero; michael@0: michael@0: public: michael@0: static gfxMatrix SetupDeviceToPatternMatrix(gfxPattern *aPattern, michael@0: const gfxMatrix& aCTM) michael@0: { michael@0: if (!aPattern) { michael@0: return gfxMatrix(); michael@0: } michael@0: gfxMatrix deviceToUser = aCTM; michael@0: deviceToUser.Invert(); michael@0: return deviceToUser * aPattern->GetMatrix(); michael@0: } michael@0: michael@0: SimpleTextContextPaint(gfxPattern *aFillPattern, gfxPattern *aStrokePattern, michael@0: const gfxMatrix& aCTM) : michael@0: mFillPattern(aFillPattern ? aFillPattern : new gfxPattern(sZero)), michael@0: mStrokePattern(aStrokePattern ? aStrokePattern : new gfxPattern(sZero)) michael@0: { michael@0: mFillMatrix = SetupDeviceToPatternMatrix(aFillPattern, aCTM); michael@0: mStrokeMatrix = SetupDeviceToPatternMatrix(aStrokePattern, aCTM); michael@0: } michael@0: michael@0: already_AddRefed GetFillPattern(float aOpacity, michael@0: const gfxMatrix& aCTM) { michael@0: if (mFillPattern) { michael@0: mFillPattern->SetMatrix(aCTM * mFillMatrix); michael@0: } michael@0: nsRefPtr fillPattern = mFillPattern; michael@0: return fillPattern.forget(); michael@0: } michael@0: michael@0: already_AddRefed GetStrokePattern(float aOpacity, michael@0: const gfxMatrix& aCTM) { michael@0: if (mStrokePattern) { michael@0: mStrokePattern->SetMatrix(aCTM * mStrokeMatrix); michael@0: } michael@0: nsRefPtr strokePattern = mStrokePattern; michael@0: return strokePattern.forget(); michael@0: } michael@0: michael@0: float GetFillOpacity() { michael@0: return mFillPattern ? 1.0f : 0.0f; michael@0: } michael@0: michael@0: float GetStrokeOpacity() { michael@0: return mStrokePattern ? 1.0f : 0.0f; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFillPattern; michael@0: nsRefPtr mStrokePattern; michael@0: michael@0: // Device space to pattern space transforms michael@0: gfxMatrix mFillMatrix; michael@0: gfxMatrix mStrokeMatrix; michael@0: }; michael@0: michael@0: #endif