PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
sdl/PlatformLabel.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/platforms/sdl/PlatformLabel.cpp
4// Purpose: Contains SDL-specific visual representation of a word
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2004-2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9//
10//
11// This file is part of the PEBL project.
12//
13// PEBL is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// PEBL is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with PEBL; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27#include "PlatformLabel.h"
28#include "../../objects/PLabel.h"
29#include "PlatformFont.h"
30#include "../../utility/rc_ptrs.h"
31#include "../../utility/PError.h"
32#include "../../base/PComplexData.h"
33#include "../../utility/PEBLUtility.h"
34#include "../../utility/FormatParser.h"
35#include "../../utility/FontCache.h"
36#ifdef PEBL_OSX
37#include "SDL.h"
38#include "SDL_ttf.h"
39#else
40#include "SDL.h"
41#include "SDL_ttf.h"
42#endif
43
44#include <stdio.h>
45#include <algorithm>
46
47// cout removed - use cerr for debug output
48using std::cerr;
49using std::endl;
50using std::flush;
51using std::list;
52using std::ostream;
53
54
55
58 PLabel(text),
59 mFontObject(font)
60{
61
62 mSurface = NULL;
63 mTexture = NULL;
65
67
68 SetFont(font);
69
70 SetText(text);
71
72 //Make the font property accessible
74
75 PComplexData * pcd = new PComplexData(myFont);
76
77 InitializeProperty("FONT",Variant(pcd));
78
79 delete pcd;
80 pcd=NULL;
81
82 InitializeProperty("WIDTH",Variant(0));
83 InitializeProperty("HEIGHT",Variant(0));
84
85 //issue draw() command to be sure the xy center is set right.
86 Draw();
87
88}
89
90
93 PLabel(label.GetText())
94
95{
97 mSurface = NULL;
99 mTexture = NULL;
100
101 SetFont(label.GetFont());
102
103
105 PComplexData * pcd = new PComplexData(myFont);
106 delete pcd;
107 pcd=NULL;
108
109 InitializeProperty("FONT",Variant(pcd));
110 InitializeProperty("WIDTH",Variant(label.GetHeight()));
111 InitializeProperty("HEIGHT",Variant(label.GetWidth()));
112 cerr << "Drawing new label in platformlabel\n";
113 Draw();
114}
115
116
119{
120 //cerr << "Label destructor\n";
121
122}
123
124
125// Inheritable function that is called by friend method << operator of PComplexData
126ostream & PlatformLabel::SendToStream(ostream& out) const
127{
128 out << "<SDL PlatformLabel: " << mText << " in " << *GetPlatformFont() << ">" <<flush;
129 return out;
130}
131
132
133
136
138{
139 // Check if formatted text mode is enabled
140 Variant formattedVar = PEBLObjectBase::GetProperty("FORMATTED");
141 bool isFormatted = (formattedVar.GetInteger() != 0);
142
143 SDL_Surface * finalSurface = NULL;
144
145 if (isFormatted) {
146 // FORMATTED TEXT RENDERING (single-line with baseline alignment)
147
148 // Parse formatted text into segments
149 std::vector<FormatParser::FormatSegment> segments = FormatParser::ParseFormattedText(mText);
150
151 // FIRST PASS: Find maximum ascent for baseline alignment
152 int maxAscent = 0;
153 int totalWidth = 0;
154
155 for (const FormatParser::FormatSegment& seg : segments) {
156 if (!seg.text.empty()) {
157 PlatformFont* segFont = GetPlatformFont();
158 std::string fontFileName = segFont->GetFontFileName();
159 int baseSize = segFont->GetFontSize();
160 PColor baseFgColor = segFont->GetFontColor();
161 PColor baseBgColor = segFont->GetBackgroundColor();
162 bool antiAliased = segFont->GetAntiAliased();
163
164 int segStyle = seg.style;
165 int segSize = seg.hasSizeOverride ? seg.sizeOverride : baseSize;
166 PColor segFgColor = seg.hasColorOverride ? seg.colorOverride : baseFgColor;
167
168 PlatformFont* renderFont = new PlatformFont(
169 fontFileName, segStyle, segSize,
170 segFgColor, baseBgColor, antiAliased);
171
172 // Get ascent and width for this segment
173 int ascent = TTF_FontAscent(renderFont->GetTTFFont());
174 if (ascent > maxAscent) maxAscent = ascent;
175
176 int segWidth, segHeight;
177 TTF_SizeText(renderFont->GetTTFFont(), seg.text.c_str(), &segWidth, &segHeight);
178 totalWidth += segWidth;
179
180 delete renderFont;
181 }
182 }
183
184 // Calculate total height (use max ascent + max descent)
185 // Note: TTF_FontDescent() returns negative values for portions below baseline
186 int maxDescent = 0;
187 for (const FormatParser::FormatSegment& seg : segments) {
188 if (!seg.text.empty()) {
189 PlatformFont* segFont = GetPlatformFont();
190 std::string fontFileName = segFont->GetFontFileName();
191 int baseSize = segFont->GetFontSize();
192 PColor baseFgColor = segFont->GetFontColor();
193 PColor baseBgColor = segFont->GetBackgroundColor();
194 bool antiAliased = segFont->GetAntiAliased();
195
196 int segStyle = seg.style;
197 int segSize = seg.hasSizeOverride ? seg.sizeOverride : baseSize;
198 PColor segFgColor = seg.hasColorOverride ? seg.colorOverride : baseFgColor;
199
200 PlatformFont* renderFont = new PlatformFont(
201 fontFileName, segStyle, segSize,
202 segFgColor, baseBgColor, antiAliased);
203
204 int descent = TTF_FontDescent(renderFont->GetTTFFont());
205 // Convert negative descent to positive for comparison
206 int absDescent = (descent < 0) ? -descent : descent;
207 if (absDescent > maxDescent) maxDescent = absDescent;
208
209 delete renderFont;
210 }
211 }
212
213 int totalHeight = maxAscent + maxDescent;
214
215 // Create surface for the entire label
216 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
217 Uint32 rmask = 0xff000000;
218 Uint32 gmask = 0x00ff0000;
219 Uint32 bmask = 0x0000ff00;
220 Uint32 amask = 0x000000ff;
221 #else
222 Uint32 rmask = 0x000000ff;
223 Uint32 gmask = 0x0000ff00;
224 Uint32 bmask = 0x00ff0000;
225 Uint32 amask = 0xff000000;
226 #endif
227
228 finalSurface = SDL_CreateRGBSurface(0, totalWidth, totalHeight, 32,
229 rmask, gmask, bmask, amask);
230
231 if (!finalSurface) {
232 return false;
233 }
234
235 // Fill with transparent background
236 SDL_FillRect(finalSurface, NULL, SDL_MapRGBA(finalSurface->format, 0, 0, 0, 0));
237
238 // SECOND PASS: Render segments with baseline alignment
239 int xOffset = 0;
240
241 for (const FormatParser::FormatSegment& seg : segments) {
242 if (!seg.text.empty()) {
243 PlatformFont* segFont = GetPlatformFont();
244 std::string fontFileName = segFont->GetFontFileName();
245 int baseSize = segFont->GetFontSize();
246 PColor baseFgColor = segFont->GetFontColor();
247 PColor baseBgColor = segFont->GetBackgroundColor();
248 bool antiAliased = segFont->GetAntiAliased();
249
250 int segStyle = seg.style;
251 int segSize = seg.hasSizeOverride ? seg.sizeOverride : baseSize;
252 PColor segFgColor = seg.hasColorOverride ? seg.colorOverride : baseFgColor;
253
254 PlatformFont* renderFont = new PlatformFont(
255 fontFileName, segStyle, segSize,
256 segFgColor, baseBgColor, antiAliased);
257
258 SDL_Surface* segSurface = renderFont->RenderText(seg.text.c_str());
259
260 if (segSurface) {
261 // Get ascent for this specific font
262 int thisAscent = TTF_FontAscent(renderFont->GetTTFFont());
263
264 // Calculate y-offset to align baseline with max baseline
265 int yOffset = maxAscent - thisAscent;
266
267 // Position segment with baseline alignment
268 SDL_Rect segRect = {xOffset, yOffset, segSurface->w, segSurface->h};
269
270 SDL_BlitSurface(segSurface, NULL, finalSurface, &segRect);
271 SDL_FreeSurface(segSurface);
272
273 xOffset += segRect.w;
274 }
275
276 delete renderFont;
277 }
278 }
279
280 } else {
281 // NORMAL (NON-FORMATTED) RENDERING
282 if(mDirection == 1) {
283 //Re-render the text using the associated font.
284 finalSurface = GetPlatformFont()->RenderText(mText.c_str());
285 } else {
286 std::string rtext = PEBLUtility::strrev_utf8(mText);
287 //Re-render the text using the associated font.
288 finalSurface = GetPlatformFont()->RenderText(rtext.c_str());
289 }
290 }
291
292 if(finalSurface) {
293 mWidth = finalSurface->w;
294 mHeight = finalSurface->h;
297 InitializeProperty("HEIGHT",mHeight);
298 InitializeProperty("WIDTH",mWidth);
299
300 //textures get created
301 if(mTexture) {
302 SDL_DestroyTexture(mTexture);
303 mTexture = NULL;
304 }
305
306 if(mRenderer) {
307 mTexture = SDL_CreateTextureFromSurface(mRenderer, finalSurface);
308 // Enable alpha blending for transparency support
309 SDL_SetTextureBlendMode(mTexture, SDL_BLENDMODE_BLEND);
310 // Enable best quality filtering (anisotropic) for zoomed textures
311 SDL_SetTextureScaleMode(mTexture, SDL_ScaleModeBest);
312
313 SDL_FreeSurface(finalSurface);
314 finalSurface = NULL;
315 }
316
317 return true;
318 } else {
319 return false;
320 }
321}
322
323
324bool PlatformLabel::SetProperty(std::string name, Variant v)
325{
326
327 if(name == "TEXT")
328 {
329
330 SetText(v);
331 }
332 else if(PLabel::SetProperty(name,v))
333 {
334 // If we set it at higher level, don't worry.
335 }
336 else if (name == "FONT")
337 {
339
340 }
341 else return false;
342
343 return true;
344}
345
347{
348
349 mFontObject = font;
350
351 // Update the FONT property so nested access works correctly
352 PComplexData * pcd = new PComplexData(mFontObject);
354 delete pcd;
355
356 mChanged =true;
357 Draw();
358
359 //if(!RenderText()) cerr << "Unable to render text.\n";
360
361}
362
363
364void PlatformLabel::SetText(const std::string & text)
365{
366 //Chain up to parent method.
368 mChanged =true;
369 Draw();
370
371 //Re-render the text onto mSurface
372 //if(!RenderText()) cerr << "Unable to render text.\n";
373
374}
375
376
378{
379
380 // Check if label text changed OR if font properties changed
381 if(mChanged || GetPlatformFont()->HasChanged())
382 {
383
384 RenderText();
385 //Reposition. This just recalculates so things are centered
386 //correctly; labels are positioned based on their center.
387
388 SetPosition(mX, mY);
389
390 InitializeProperty("HEIGHT",mHeight);
391 InitializeProperty("WIDTH",mWidth);
392 GetPlatformFont()->ClearChanged(); // Clear font changed flag after re-rendering
393
394 // Only clear mChanged if texture was successfully created
395 // If mTexture is NULL, keep mChanged=true to retry when renderer is available
396 if(mTexture)
397 {
398 mChanged = false;
399 }
400 }
401 return PlatformWidget::Draw();
402
403}
#define NULL
Definition BinReloc.cpp:317
@ CDT_LABEL
Definition PEBLObject.h:59
counted_ptr< PEBLObjectBase > GetObject() const
virtual bool InitializeProperty(std::string name, Variant v)
virtual bool SetProperty(std::string name, Variant v)
ComplexDataType mCDT
Definition PEBLObject.h:109
Variant GetProperty(std::string) const
virtual PColor GetBackgroundColor() const
Definition PFont.cpp:296
virtual PColor GetFontColor() const
Definition PFont.cpp:289
virtual std::string GetFontFileName() const
Definition PFont.h:83
virtual bool GetAntiAliased() const
Definition PFont.h:88
virtual int GetFontSize() const
Definition PFont.h:85
virtual bool SetProperty(std::string, Variant v)
Definition PLabel.cpp:69
std::string mText
Definition PTextObject.h:70
virtual void SetText(const std::string &text)
virtual pInt GetWidth() const
Definition PWidget.h:85
virtual pInt GetHeight() const
Definition PWidget.h:86
pInt mWidth
Definition PWidget.h:136
pInt mX
Definition PWidget.h:125
pInt mHeight
Definition PWidget.h:136
pInt mY
Definition PWidget.h:125
SDL_Surface * RenderText(const std::string &text)
This takes care of all the busy work of rendering the text.
TTF_Font * GetTTFFont() const
void ClearChanged()
Clear all changed flags.
Validator platform label - no rendering, used only for compilation.
virtual bool Draw()
This method initiates everything needed to display the main window
virtual void SetText(const std::string &text)
PlatformLabel(const std::string &text, counted_ptr< PEBLObjectBase > font)
virtual counted_ptr< PEBLObjectBase > GetFont()
virtual std::ostream & SendToStream(std::ostream &out) const
An inheritable printing class used by PEBLObjectBase::operator<<.
virtual void SetPosition(pInt x, pInt y)
This sets the widget's position on its parent widget.
virtual bool RenderText()
virtual ~PlatformLabel()
Standard Destructor.
virtual bool SetProperty(std::string, Variant v)
virtual void SetFont(counted_ptr< PEBLObjectBase > font)
SDL_Surface * mSurface
SDL_Texture * mTexture
virtual bool Draw()
This method initiates everything needed to display the main window
SDL_Renderer * mRenderer
pInt GetInteger() const
Definition Variant.cpp:997
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
std::vector< FormatSegment > ParseFormattedText(const std::string &input, int charWidth)
Parse formatted text into segments.
void strrev_utf8(char *p)