blob: 47237a905e424ae07a32a9711007bd012051b88e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkTextBox.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000010
11static inline int is_ws(int c)
12{
13 return !((c - 1) >> 5);
14}
15
16static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
17{
bungeman@google.com6fccbd12012-07-24 16:08:45 +000018 return paint.breakText(text, stop - text, margin);
reed@android.com8a1c16f2008-12-17 15:59:43 +000019}
20
21int SkTextLineBreaker::CountLines(const char text[], size_t len, const SkPaint& paint, SkScalar width)
22{
23 const char* stop = text + len;
24 int count = 0;
25
26 if (width > 0)
27 {
28 do {
29 count += 1;
30 text += linebreak(text, stop, paint, width);
31 } while (text < stop);
32 }
33 return count;
34}
35
36//////////////////////////////////////////////////////////////////////////////
37
38SkTextBox::SkTextBox()
39{
40 fBox.setEmpty();
41 fSpacingMul = SK_Scalar1;
42 fSpacingAdd = 0;
43 fMode = kLineBreak_Mode;
44 fSpacingAlign = kStart_SpacingAlign;
45}
46
47void SkTextBox::setMode(Mode mode)
48{
49 SkASSERT((unsigned)mode < kModeCount);
50 fMode = SkToU8(mode);
51}
52
53void SkTextBox::setSpacingAlign(SpacingAlign align)
54{
55 SkASSERT((unsigned)align < kSpacingAlignCount);
56 fSpacingAlign = SkToU8(align);
57}
58
59void SkTextBox::getBox(SkRect* box) const
60{
61 if (box)
62 *box = fBox;
63}
64
65void SkTextBox::setBox(const SkRect& box)
66{
67 fBox = box;
68}
69
70void SkTextBox::setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom)
71{
72 fBox.set(left, top, right, bottom);
73}
74
75void SkTextBox::getSpacing(SkScalar* mul, SkScalar* add) const
76{
77 if (mul)
78 *mul = fSpacingMul;
79 if (add)
80 *add = fSpacingAdd;
81}
82
83void SkTextBox::setSpacing(SkScalar mul, SkScalar add)
84{
85 fSpacingMul = mul;
86 fSpacingAdd = add;
87}
88
89/////////////////////////////////////////////////////////////////////////////////////////////
90
91void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPaint& paint)
92{
93 SkASSERT(canvas && &paint && (text || len == 0));
94
95 SkScalar marginWidth = fBox.width();
96
97 if (marginWidth <= 0 || len == 0)
98 return;
99
100 const char* textStop = text + len;
101
102 SkScalar x, y, scaledSpacing, height, fontHeight;
103 SkPaint::FontMetrics metrics;
104
105 switch (paint.getTextAlign()) {
106 case SkPaint::kLeft_Align:
107 x = 0;
108 break;
109 case SkPaint::kCenter_Align:
110 x = SkScalarHalf(marginWidth);
111 break;
112 default:
113 x = marginWidth;
114 break;
115 }
116 x += fBox.fLeft;
117
118 fontHeight = paint.getFontMetrics(&metrics);
119 scaledSpacing = SkScalarMul(fontHeight, fSpacingMul) + fSpacingAdd;
120 height = fBox.height();
121
122 // compute Y position for first line
123 {
124 SkScalar textHeight = fontHeight;
125
126 if (fMode == kLineBreak_Mode && fSpacingAlign != kStart_SpacingAlign)
127 {
128 int count = SkTextLineBreaker::CountLines(text, textStop - text, paint, marginWidth);
129 SkASSERT(count > 0);
130 textHeight += scaledSpacing * (count - 1);
131 }
132
133 switch (fSpacingAlign) {
134 case kStart_SpacingAlign:
135 y = 0;
136 break;
137 case kCenter_SpacingAlign:
138 y = SkScalarHalf(height - textHeight);
139 break;
140 default:
141 SkASSERT(fSpacingAlign == kEnd_SpacingAlign);
142 y = height - textHeight;
143 break;
144 }
145 y += fBox.fTop - metrics.fAscent;
146 }
147
148 for (;;)
149 {
150 len = linebreak(text, textStop, paint, marginWidth);
151 if (y + metrics.fDescent + metrics.fLeading > 0)
152 canvas->drawText(text, len, x, y, paint);
153 text += len;
154 if (text >= textStop)
155 break;
156 y += scaledSpacing;
157 if (y + metrics.fAscent >= height)
158 break;
159 }
160}
161
reed@android.com033e03c2010-05-18 21:17:43 +0000162///////////////////////////////////////////////////////////////////////////////
163
164void SkTextBox::setText(const char text[], size_t len, const SkPaint& paint) {
165 fText = text;
166 fLen = len;
167 fPaint = &paint;
168}
169
170void SkTextBox::draw(SkCanvas* canvas) {
171 this->draw(canvas, fText, fLen, *fPaint);
172}
173
174int SkTextBox::countLines() const {
175 return SkTextLineBreaker::CountLines(fText, fLen, *fPaint, fBox.width());
176}
177
178SkScalar SkTextBox::getTextHeight() const {
179 SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
180 return this->countLines() * spacing;
181}
182