/* ** ClanLib SDK ** Copyright (c) 1997-2005 The ClanLib Team ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** ** Note: Some of the libraries ClanLib may link to may have additional ** requirements or restrictions. ** ** File Author(s): ** ** Magnus Norddahl ** (if your name is missing here, please add it) */ //! clanDisplay="Fonts" //! header=display.h #ifndef header_glyph_buffer #define header_glyph_buffer #ifdef CL_API_DLL #ifdef CL_DISPLAY_EXPORT #define CL_API_DISPLAY __declspec(dllexport) #else #define CL_API_DISPLAY __declspec(dllimport) #endif #else #define CL_API_DISPLAY #endif #if _MSC_VER > 1000 #pragma once #endif #ifdef _MSC_VER #pragma warning( disable : 4786) #endif #include #include #include #include "font.h" #include "../Core/Math/origin.h" #include "color.h" #if defined(__APPLE__) && defined(verify) // I guess sometimes "verify" is defined on OSX by some system headers // it manifests itself with an odd error on the following line (below): // void verify(const CL_GlyphBuffer& gb); // there might be a better solution to this, but it seems to work out #undef verify #endif class CL_Point; class CL_Rect; class CL_GraphicContext; //: Stores and draws glyphs in one or more fonts. //- !group=Display/Fonts! //- !header=display.h! //-

CL_GlyphBuffer is mostly useful for text effects, for concatenating //- multiple CL_Font or CL_TextStyler draw_to_gb()s, or for //- when you need access to the low-level glyph positioning data. You can //- also use it to save time by only rendering a bit of text once when you need //- to, for example, both draw that text and know its bounding rectangle. But //- for optimiziation purposes alone when dealing with unchanging text, you //- should instead //- have CL_Font or CL_TextStyler render onto a CL_Canvas or CL_Surface.

//-

A glyph buffer consists of a series of characters and positions, //- and markers for font changes and various other text-altering infomration. The internal data structures used //- are made accessible and mutable externally. CL_Font //- and CL_TextStyler both have draw_to_gb() methods, which allows them to //- put data into a CL_GlyphBuffer rather than drawing to a CL_GraphicContext.

//-

The effect maps for color, angle, and scale allow you to affect those properties //- for individual glyphs during drawing and bounding rect calculation. The angle and scale maps are different //- from the GlyphBuffer angle and scale properties; the properties apply to the entire CL_GlyphBuffer as a whole, while entries //- in the effect maps affect individual glyphs seperately. It's not legal to insert //- an effect map entry for a glyph that doesn't exist.

//-

Unlike scale effects map entries, which are multiplied by the CL_Font's existing scale, //- color effect map entries entirely override the CL_Font's color. No blending between //- the two colors is performed. Also, it's your responsibility to make sure that CL_Fonts have the right //- blending functions.

//-

In functions that calculate the size or bounding rectangle of the GlyphBuffer //- (such as bouding_rect(), get_size(), and the alignment calculations within draw), //- effect properties do take effect on the final rectangle, but GlyphBuffer rotation //- and scaling (set with CL_GlyphBuffer::set_angle(), and CL_GlyphBuffer::set_scale(), //- respectively) do not. This is consistent with the behavior of CL_Sprite and CL_Surface; the content //- affects bounding rect calculation, but the other properties of the object //- do not.

class CL_API_DISPLAY CL_GlyphBuffer { //! Construction: public: //: Constructs a glyph buffer. CL_GlyphBuffer(); virtual ~CL_GlyphBuffer() {;} //! Subclasses: public: //: A character and position describing a glyph in the buffer, stored in the glyphs vector. struct Glyph { //:Constructs a new Glyph //p: The CL_Point that will be assigned to pos //c: The unsigned int that will be assigned to character Glyph() {;} Glyph(CL_Point p, unsigned int c) : pos(p), character(c) {;} //: The position where the glyph is drawn. CL_Point pos; //: The character which determines the glyph that's drawn. unsigned int character; }; //: Represents a subsection of the buffer struct Range { //:Constructs a new Range. //new_start: The starting index of the range, inclusive //new_end: The ending index of the range, exclusive //gb: Constructs a new Range that encompasses this entire CL_GlyphBuffer Range() : start(0), end(0) {;} Range(const CL_GlyphBuffer& gb) : start(0), end(int(gb.get_glyphs().size())) {;} Range(int new_start, int new_end) : start(new_start), end(new_end) {;} //: Makes range valid for a given GlyphBuffer. //-

This is automatically called when you pass the range to any GlyphBuffer method.

void verify(const CL_GlyphBuffer& gb); //: Pulls in the range at the beginning until it reaches a drawable glyph void remove_invis_left(const CL_GlyphBuffer& gb); //: Pulls in the range at the end until it reaches a drawable glyph void remove_invis_right(const CL_GlyphBuffer& gb); //: Pulls in the range at both sides until it reaches a drawable glyph void remove_invis_both(const CL_GlyphBuffer& gb) {remove_invis_left(gb); remove_invis_right(gb);} //The starting index of the range, inclusive. int start; //The ending index of the range, exclusive. int end; }; //! Attributes: public: //: Returns a reference to the glyphs in the buffer and their drawing positions. //-

See CL_GlyphBuffer::Glyph for details./p> std::vector& get_glyphs() {return glyphs;} const std::vector& get_glyphs() const {return glyphs;} //: Returns a reference to the font map. //-

Elements of the glyphs vector before the first font marker are ignored. Each element //- within this map causes a change in the font while drawing, where the key of the //- element is the index in the glyphs vector before which the font is changed.

std::map& get_font_markers() {return font_markers;} const std::map& get_font_markers() const {return font_markers;} //: Returns a reference to the color effect map. std::map& get_color_effects() {return color_effects;} const std::map& get_color_effects() const {return color_effects;} //: Returns a reference to the x scale effect map. //-

Scaling occurs away from the upper-left corner of each glyph.

std::map& get_scale_x_effects() {return scale_x_effects;} const std::map& get_scale_x_effects() const {return scale_x_effects;} //: Returns a reference to the y scale effect map. //-

Scaling occurs away from the upper-left corner of each glyph.

std::map& get_scale_y_effects() {return scale_y_effects;} const std::map& get_scale_y_effects() const {return scale_y_effects;} //: Returns a reference to the character angle effect map. std::map& get_angle_effects() {return angle_effects;} const std::map& get_angle_effects() const {return angle_effects;} //: Returns an iterator to the font_marker associated with a particular glyph. //-

If a negative index is supplied, assumes glyph 0. If an index //- past the last element is supplied, assumes the last element. If //- there are no CL_Fonts in the GlyphBuffer, or if there is no CL_Font //- associated with that glyph, returns the end iterator.

std::map::iterator get_font_for(int glyph_index); std::map::const_iterator get_font_for(int glyph_index) const; //: Returns the width of the entire buffer, or a sub-Range. //return: The width in pixels. //param range: A subsection of the glyphs vector to get the width of. //param idx: The vector index of a single glyph to get the width of. //param ignore_invis: If true, ignores invisible glyphs (glyphs without an actual visual glyph in the font) int get_width(bool ignore_invis = true) const {return get_width(Range(*this), ignore_invis);} int get_width(int idx, bool ignore_invis = true) const {return get_width(Range(idx, idx+1), ignore_invis);} int get_width(Range range, bool ignore_invis = true) const {return get_size(range, ignore_invis).width;} //: Returns the height of the entire buffer, or a sub-Range. //return: The height in pixels. //param range: A subsection of the glyphs vector to get the height of. //param idx: The vector index of a single glyph to get the height of. //param ignore_invis: If true, ignores invisible glyphs (glyphs without an actual visual glyph in the font) int get_height(bool ignore_invis = true) const {return get_height(Range(*this), ignore_invis);} int get_height(int idx, bool ignore_invis = true) const {return get_height(Range(idx, idx+1), ignore_invis);} int get_height(Range range, bool ignore_invis = true) const {return get_size(range, ignore_invis).height;} //: Returns the size of the entire buffer, or a sub-Range. //return: The size in pixels. //param range: A subsection of the glyphs vector to get the size of. //param idx: The vector index of a single glyph to get the size of. //param ignore_invis: If true, ignores invisible glyphs (glyphs without an actual visual glyph in the font) CL_Size get_size(bool ignore_invis = true) const {return get_size(Range(*this), ignore_invis);} CL_Size get_size(int idx, bool ignore_invis = true) const {return get_size(Range(idx, idx+1), ignore_invis);} CL_Size get_size(Range range, bool ignore_invis = true) const {return internal_rect(range, ignore_invis).get_size();} //: Returns current angle. //-

This rotates the entire glyph field when drawing. //- The fonts' glyph rotation origin must be origin_top_left:0:0 for this //- to produce legible results. See CL_Font::set_glyph_rotation_hotspot().

float get_angle() const {return rot_angle;} //: Returns scale for x and y. //-

1.0f is normal scale, 2.0f is twice the size, etc.

void get_scale(float &x, float &y) const {x = scale_x; y = scale_y;} //: Returns translation hotspot for the entire glyph field. void get_alignment(CL_Origin &origin, int &x, int &y) const {origin = trans_origin; x = trans_x; y = trans_y;} //: Returns rotation hotspot for the entire glyph field. void get_rotation_hotspot(CL_Origin &origin, int &x, int &y) const {origin = rot_origin; x = rot_x; y = rot_y;} //: Returns the area that would be drawn to if a draw were performed. //return: The area into which any pixels would be drawn. If buffer is empty, returns a rectangle with zero size. //param x, y: Anchor position to start "drawing" at. Actual rendering position depends on the alignment mode. //param dest: Rectangle to render glyphs in. The glyphs will be positioned within the rectangle depending on the alignment mode. //param range: The subsection of the glyphs vector to use. //param ignore_invis: If true, ignores invisible glyphs (glyphs without an actual visual glyph in the font) CL_Rect bounding_rect(int x = 0, int y = 0, bool ignore_invis = true) const {return bounding_rect(Range(*this), x, y, ignore_invis);} CL_Rect bounding_rect(Range range, int x = 0, int y = 0, bool ignore_invis = true) const {return bounding_rect(range, CL_Rect(x, y, x, y), ignore_invis);} CL_Rect bounding_rect(CL_Rect dest, bool ignore_invis = true) const {return bounding_rect(Range(*this), dest, ignore_invis);} CL_Rect bounding_rect(Range range, CL_Rect dest, bool ignore_invis = true) const; //: Returns the area occupied by glyphs inside the glyph buffer's coordinate space. //return: The area occupied by all glyphs, inside the buffer. If buffer is empty, returns a rectangle with zero size. //param range: The subsection of the glyphs vector to get the internal bounding rect of. //param idx: The vector index of a single glyph to get the internal bounding rect of. //param ignore_invis: If true, ignores invisible glyphs (glyphs without an actual visual glyph in the font) CL_Rect internal_rect(bool ignore_invis = true) const {return internal_rect(Range(*this), ignore_invis);} CL_Rect internal_rect(int idx, bool ignore_invis = true) const {return internal_rect(Range(idx, idx+1), ignore_invis);} CL_Rect internal_rect(Range range, bool ignore_invis = true) const; //: Returns a vector of CL_GlyphBuffer::Range, each of which contains one line. //param range: The subsection of the glyphs vector to look through, which is expanded both ways. //-

The CL_GlyphBuffer's contents (the glyphs vector, the font markers map, and the effects maps), //- if any, must not have been created/altered by anything but CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() //- or the CL_GlyphBuffer justification functions or using CL_GlyphBuffer::remove to take glyphs off the end //- for this method to work.

std::vector get_lines() const {return get_lines(Range(*this));} std::vector get_lines(Range range) const; //: Returns a single CL_GlyphBuffer::Range containing the line that contains a given glyph. //param idx: The index in the glpyhs vector of the glyph. //-

The CL_GlyphBuffer's contents (the glyphs vector, the font markers map, and the effects maps), //- if any, must not have been created/altered by anything but CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() //- or the CL_GlyphBuffer justification functions or using CL_GlyphBuffer::remove to take glyphs off the end //- for this method to work.

CL_GlyphBuffer::Range get_line(int idx) const; //! Operations public: //: Add elements onto the end of the glyphs vector. //return: A reference to this object, allowing you to chain multiple calls of add() together. //param x, y: The position to add the characters at (always upper-left positioning inside the buffer). //param center_x, center_y: Where to realign the origin of the input GlyphBuffer to in this GlyphBuffer. //param character: A single character to add without preprocessing. //param other: Another CL_GlyphBuffer to copy all glyphs and data from to the specified position. //param range: The subsection of the other GlyphBuffer to add onto the end of this one //-

It's usually more useful and efficient to have ClanLib draw many glyphs into the GlyphBuffer at once using //- CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() than to use this function or the glyphs vector //- to manually add glyphs.

CL_GlyphBuffer &add(int x, int y, unsigned int character) {glyphs.push_back(Glyph(CL_Point(x,y), character)); return *this;} CL_GlyphBuffer &add(const CL_GlyphBuffer &other, int center_x = 0, int center_y = 0) {return add(other, Range(other), center_x, center_y);} CL_GlyphBuffer &add(const CL_GlyphBuffer &other, Range range, int center_x = 0, int center_y = 0); //: Removes an element or elements from the glyphs vector, reorganizes the font and effect maps to compensate. //return: A reference to this object, allowing you to chain multiple calls of remove() together. //param index: The index in the glyphs vector to remove (all elements beyond this are slid back) //param range: The subsection of the glyphs vector to erase CL_GlyphBuffer &remove(int index) {return remove(Range(index, index+1));} CL_GlyphBuffer &remove(Range range); //: Adds a font change marker, set to occur before the next glyph/location you add is drawn. //return: A reference to this object, allowing you to chain change_font() into chains of calls to add(). //param font: The font to change to. CL_GlyphBuffer &change_font(const CL_Font &font); //: Remove all the character position and font marker data in the buffer. void clear(); //: Draw the glyphs in the buffer. //param x, y: Anchor position to draw at. Actual drawing position depends on the alignment mode. //param gc: Graphic context on which to render. If null, will use CL_Display's current graphic context. //param dest: Rectangle to draw glyphs in. The glyphs will be positioned within the rectangle depending on the alignment mode. //param range: The subsection of the glyphs vector to use. void draw( int x = 0, int y = 0, CL_GraphicContext *gc = 0) {draw(Range(*this), CL_Rect(x, y, x, y), gc);} void draw( Range range, int x = 0, int y = 0, CL_GraphicContext *gc = 0) {draw(range, CL_Rect(x, y, x, y), gc);} void draw( CL_Rect dest, CL_GraphicContext *gc = 0) {draw(Range(*this), dest, gc);} void draw( Range range, CL_Rect dest, CL_GraphicContext *gc = 0) {CL_Rect irect = internal_rect(range); draw_glyphs(range, i2e_offset(irect, dest), irect, gc);} //: Draws the glyphs in the buffer without bounding rect alignment. //param x, y: Glyphs are drawn with the CL_GlyphBuffer's internal origin at this location. //param gc: Graphic context on which to render. If null, will use CL_Display's current graphic context. //param range: The subsection of the glyphs vector to use. void fixed_draw( int x = 0, int y = 0, CL_GraphicContext *gc = 0) {fixed_draw(Range(*this), x, y, gc);} void fixed_draw( Range range, int x = 0, int y = 0, CL_GraphicContext *gc = 0) {draw_glyphs(range, CL_Point(x, y), CL_Rect(0,0,0,0), gc);} //: Rejustifies lines in the GlyphBuffer to the left. //param range: The subsection of the glyphs vector to change, which is expanded out both ways to contain at least one line. //param left: The x coordinate of the vertical line that text should be moved up against. //-

The CL_GlyphBuffer's contents (the glyphs vector, the font markers map, and the effects maps), //- if any, must not have been created/altered by anything but CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() //- or the CL_GlyphBuffer justification functions or using CL_GlyphBuffer::remove to take glyphs off the end //- for this method to work.

//-

To preserve indentation, this method takes into account whitespace (glyphs not provided by the font) when justifying.

void justify_left(int left = 0) {justify_left(Range(*this), left);} void justify_left(Range range, int left = 0); //: Rejustifies lines in the GlyphBuffer to the center. //param range: The subsection of the glyphs vector to change, which is expanded out both ways to contain at least one line. //param center: The x coordinate of the vertical line that text should be centered at. //-

The CL_GlyphBuffer's contents (the glyphs vector, the font markers map, and the effects maps), //- if any, must not have been created/altered by anything but CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() //- or the CL_GlyphBuffer justification functions or using CL_GlyphBuffer::remove to take glyphs off the end //- for this method to work.

//-

This method ignores whitespace (glyphs not provided by the font) when justifying.

void justify_center(int center = 0) {justify_center(Range(*this), center);} void justify_center(Range range, int center = 0); //: Rejustifies lines in the GlyphBuffer to the right. //param range: The subsection of the glyphs vector to change, which is expanded out both ways to contain at least one line. //param right: The x coordinate of the vertical line that text should be moved up against. //-

The CL_GlyphBuffer's contents (the glyphs vector, the font markers map, and the effects maps), //- if any, must not have been created/altered by anything but CL_Font::draw_to_gb() or CL_TextStyler::draw_to_gb() //- or the CL_GlyphBuffer justification functions or using CL_GlyphBuffer::remove to take glyphs off the end //- for this method to work.

//-

This method ignores whitespace (glyphs not provided by the font) when justifying.

void justify_right(int right = 0) {justify_right(Range(*this), right);} void justify_right(Range range, int right = 0); //: Set absolute rotation angle. //-

This rotates the entire glyph field when drawing. //- The fonts' glyph rotation origin must be origin_top_left:0:0 for this //- to produce legible results. See CL_Font::set_glyph_rotation_hotspot().

void set_angle(float angle); //: Add angle to current angle. //-

This rotates the entire glyph field when drawing. //- The fonts' glyph rotation origin must be origin_top_left:0:0 for this //- to produce legible results. See CL_Font::set_glyph_rotation_hotspot().

void rotate(float angle) {angle += rot_angle; set_angle(angle);} //: Set scale for x and y directions individually. //-

1.0f is normal scale, 2.0f is twice the size, etc.

void set_scale(float x, float y) {scale_x = x; scale_y = y;} //: Sets translation hotspot. void set_alignment(CL_Origin origin, int x = 0, int y = 0) {trans_origin = origin; trans_x = x; trans_y = y;} //: Sets rotation hotspot. void set_rotation_hotspot(CL_Origin origin, int x = 0, int y = 0) {rot_origin = origin; rot_x = x; rot_y = y;} //! Implementation: private: //: Used to iterate through the buffer, changing font and applying effects as needed class Iter; friend class Iter; class Iter { private: //Pointer to the GlyphBuffer const CL_GlyphBuffer* pgb; //The current index in the glyphs vector int glyph_num; //Do not allow default construction Iter() {} public: //Constructs a new Iter //param range: The beginning index in this Range is used as the initial glyph_num Iter(const CL_GlyphBuffer& new_gb, Range range) : pgb(&new_gb), glyph_num(range.start) {} //Advances to the next glyph (prefix) Iter& operator ++() {++glyph_num; return *this;} //Advances to the next glyph (postfix) const Iter operator ++(int) {Iter old = *this; ++(*this); return old;} //Returns true if the iterator has covered everything (i.e. is past-the-end) //param range: The ending index in this Range is checked against bool at_end(Range range) const {if (glyph_num >= range.end) return true; return false;} //Returns the index in the glyphs vector int get_glyph_num() const {return glyph_num;} //Returns the scale effect, or 1.0 if no scale effect for this glyph void get_scale_eff(float& x, float& y) const; //Returns the angle effect, or 0.0 if no angle effect for this glyph float get_angle_eff() const; //Returns the color effect, or given font's color if no color effect for this glyph CL_Color get_color_eff(const CL_Font& fnt) const; //Returns a non-mutable reference to the current font const CL_Font& get_font() const; }; //: Returns true if the given glyph index is directly before a linebreak, manual or logical //param fnt: The font that idx is in bool is_linebreak(int idx, const CL_Font& fnt) const; //: Erases all elements of a map in [start, end), then slides back elements template static void cut_range(std::map& target, int start, int end) { //Remove all the elements inside the range target.erase(target.lower_bound(start), target.upper_bound(end)); //Move back all the elements from the end of the range to the end of the map for (typename std::map::iterator it = target.lower_bound(end); it != target.end();) { target[it->first - (end - start)] = it->second; target.erase(it++); } } //: Returns the offset to align an internal bounding rectangle to an external bounding rectangle. CL_Point i2e_offset(CL_Rect src, CL_Rect dest) const; //: Returns the point to scale away from. //param irect: Internal rectangle containing all glyphs CL_Point get_scale_hotspot(CL_Rect irect) const; //: Returns the point to rotate around. //param irect: Internal rectangle containing all glyphs CL_Point get_rot_hotspot(CL_Rect irect) const; //: Draws all the glyphs in the buffer. //param offset: Added to each position right before drawing, to move to external coords //param irect: Result of internal_rect() passed in if caller has it, for efficiency's sake, else CL_Rect(0,0,0,0) //param gc: The graphics context to draw to (default if 0) //param range: The subsection of the glyphs vector to use void draw_glyphs( Range range, CL_Point offset, CL_Rect irect, CL_GraphicContext* gc) const; //: Scale. float scale_x; float scale_y; //: Angle and rotation hotspot. float rot_angle; CL_Origin rot_origin; int rot_x; int rot_y; //: Alignment. CL_Origin trans_origin; int trans_x; int trans_y; //: The glyphs and their positions. std::vector glyphs; //: The font change markers. mutable std::map font_markers; //: Color, scale, and angle font effect maps. std::map color_effects; std::map scale_x_effects; std::map scale_y_effects; std::map angle_effects; }; #endif