everyone!
I'am trying to writing a custom debugger for my own hardware. A problem I face is that I don't know how to realize the add debug point function.
In details, I want to enter (F9) in one line, and tag a red point on the left column. Or, I double click the left column and tag a red point.
I have learned the code Editor Example provided by Qt itself, but I have no idea how to realize my design.
Some tiny example project would be better.
The red point looks like this
For simplicity the red circles will be added when clicking in the right space where the numbers are similar to QtCreator, the following procedure can be extrapolated to your specific requirement.
The first thing to get is the position of the cursor with respect to the editor, actually get the height or rectangle where the line that we want to find is located, in the case of mousePressEvent returns the position of the click with respect to the widget that draws the numbers, with this height we can get the position with the following algorithm, then store it if it does not exist or delete it if it was already stored (h is the position relative to the top of the widget):
QTextBlock block = firstVisibleBlock();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
int blockNumber = block.blockNumber();
while (block.isValid()) {
if (block.isVisible()) {
if(h > top && h<bottom){
int index = breakpoints.indexOf(blockNumber);
if(index != -1){
breakpoints.remove(index);
}
else
breakpoints<<blockNumber;
update();
return;
}
}
blockNumber++;
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
}
Then in the method that paints the numbers, in this case lineNumberAreaPaintEvent, as there we have the blocks and their respective index, it is compared and if one of the stored numbers is painted.
void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
//add next lines
if(breakpoints.indexOf(blockNumber) != -1){
painter.setBrush(Qt::red);
painter.drawEllipse(0, top + (fontMetrics().height()-width_circle)/2, width_circle, width_circle);
}
//end lines
painter.setPen(Qt::black);
painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
}
The following image shows the result.
The complete example can be found in the following link.
Related
Link to project
The interesting parts should be in gameengine.cpp's "launchSplash" -function and splashanimation.cpp
The game creates the bubbles randomly in the acceptable are. The player's job is to shoot the bubbles with water drops. The water drops are launched from the middle bottom part of the game screen. The grids are only used for debugging, and will later on be gone, but it makes visualizing the areas easier.
The bubbles are destroyed by shooting the water drop at them, but the water drop disappears when it hits a bubble or the upper boundary of the game. The water drop shoots to the direction of a mouse click.
I'm trying to create a collision detection for a basic bubble shooter game, but I'm not sure how I can detect the collision in a neat way.
The game board looks something like this game board, the water drops are shot from the middle bottom part of the screen to the direction of the cursor.
Eventually I'll have the water drop ricochet from the walls, but at the moment I'm contempt with figuring out how to detect collisions in the first place.
The game board is 500x600 units (width x height), so the point the water drop is shot at is (250, 600).
When the water drop is shot, I use
void GameEngine::launchSplash(int clickX, int clickY){
// <my long method of calculating the coordinates for the water drop's path>
graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), xDestination, yDestination);
}
where xDestination and yDestionation are the place the water drop will end up if it travels unhindered. The water drop will either end up at x=0 / x=500 and/or y=0/y=600, but I don't think that's relevant.
The bubbles are added to the game board with
board_.clear();
for(int y = 0; y < HEIGHT; ++y)
{
std::vector< std::shared_ptr<Bubble> > row;
for(int x = 0; x < WIDTH; ++x)
{
std::shared_ptr<Bubble> newBubble = nullptr;
// There will be bubbles only in the top 3/4 of the board only in the middle
// (i.e. not in the first two and last two columns).
if (y < HEIGHT*3/4 && x > 1 && x < WIDTH-2)
{
// Generate random numbers using the enumearation type defined in
// file bubble.hh. The value COLOR_COUNT is used to represent no bubble.
std::uniform_int_distribution<int> distribution(RED,COLOR_COUNT);
// If you want no empty squares, change the initialization to:
// std::uniform_int_distribution<int> distribution(RED,BLUE);
Color color = static_cast<Color>(distribution(randomEngine_));
if(color != COLOR_COUNT) {
newBubble = std::make_shared<Bubble>(x, y, color);
}
}
row.push_back(newBubble);
}
board_.push_back(row);
}
in the gameengine.cpp which draws the board and the water drop being shot at the bubbles.
The water drops are drawn using
SplashAnimation::SplashAnimation(GameBoard* scene, QPointF startPoint, QPointF endPoint):
QVariantAnimation(0),
scene_(scene),
item_()
{
scene_->addItem(&item_);
// The animation runs for the given duration and moves the splash
// smoothly from startpoint to endpoint.
setDuration(2000);
setKeyValueAt(0, QPointF(startPoint));
setKeyValueAt(1, QPointF(endPoint));
}
I figured there are two ways to do this: either the inbuilt QT Collision detection or doing a separate calculation. I was not able to work with QT Collision and my attempts at detecting the collision manually did not really work out too well.
I already have a function for detecting bubble objects at certain cells, but it's in column/row instead of raw coordinates (500x600).
std::shared_ptr<Bubble> GameEngine::bubbleAt(int x, int y) const
{
if (0 <= x and x < WIDTH and 0 <= y and y < HEIGHT){
return board_.at(y).at(x);
}
else{
return nullptr;
}
}
Edit: Currently I'm trying to work on something like this, but I'm afraid it's going to be a bit heavy for the game since it iterates so much (or not?):
for (int i = 0; i<600;++i)
{
xfract = (xDestination+250.0)/600.0;
yfract = (600.0-yDestination)/600.0;
xStep = xfract*i;
yStep = yfract*i;
if (xStep >= 50){
thisX = xStep/50-5;
}else{
thisX=5;
}
if (yStep >= 50){
thisY = 11-yStep/50 + 1;
}else{
thisY = 11;
}
thisX = abs(thisX);
if (bubbleAt(thisX, thisY)!=nullptr){
endX = xfract*i;
endY = yfract*i;
i = 600;
std::cout << "collision at x: "<<thisX<< " y: "<<thisY<<std::endl;
std::cout << "collision at x: "<<xStep<< " y: "<<yStep<<std::endl;
std::cout << graphicalGameBoard_.width() << " " << graphicalGameBoard_.height()<<std::endl;
removeBubble(thisX, thisY);
graphicalGameBoard_.removeBubble(thisX, thisY);
endY = 600-endY;
}
}
graphicalGameBoard_.animateSplash(graphicalGameBoard_.width()/2, graphicalGameBoard_.height(), endX, endY);
I'm trying to split the steps into small fractions and check if the current step has a bubble in it until the water drop reaches the end or finds a bubble.
This works on my only one of my sides in terms of calculating, but the animation is off the mark and right side (x>250) collision detection doesn't work at all for some reason (it hits seemingly random bubbles at impossible locations on the right side).
Edit^2: Here are things I've tried in order to work with the actual collision detection of QT:
Within splashanimation.cpp, where the water drop is drawn using
SplashAnimation::SplashAnimation(GameBoard* scene, QPointF startPoint, QPointF endPoint):
QVariantAnimation(0),
scene_(scene),
item_()
{
scene_->addItem(&item_);
// The animation runs for the given duration and moves the splash
// smoothly from startpoint to endpoint.
setDuration(2000);
setKeyValueAt(0, QPointF(startPoint));
setKeyValueAt(1, QPointF(endPoint));
}
SplashAnimation::~SplashAnimation()
{
scene_->removeItem(&item_);
}
void SplashAnimation::updateCurrentValue(QVariant const& value)
{
item_.setPos(value.toPointF());
}
where scene is QGraphicsScene, and the parent of this contains the bubbles.
I've tried this on both the gameboard.cpp (which is parent for bubbles and the animation) and splash.cpp (which animates the water drop), but both give me the same compilation errors.
QGraphicsItem::QGraphicsItem();
gives
error: cannot call constructor ?QGraphicsItem::QGraphicsItem? directly [-fpermissive]
QGraphicsItem::QGraphicsItem();
^
QList<QGraphicsItem *> list = collidingItems() ;
gives error: ?collidingItems? was not declared in this scope
QList<QGraphicsItem *> list = collidingItems() ;
^
QList<QGraphicsItem *> list = QGraphicsItem::collidingItems() ;
gives error: cannot call member function ?QList<QGraphicsItem*> QGraphicsItem::collidingItems(Qt::ItemSelectionMode) const? without object
QList<QGraphicsItem *> list = QGraphicsItem::collidingItems() ;
^
I also tried adding arguments, but nothing I had the mind to try worked any better.
In this answer I am going to give you some recommendations that you use to implement the solution:
Avoid using the following instruction, use the signals that is one of the most powerful elements of Qt and that the event loop does the work.
while (animations_.state() == QAbstractAnimation::Running)
{
QCoreApplication::processEvents(QEventLoop::AllEvents);
}
QGraphicsScene works with coordinates that support the floating point, so the coordinates of the scene are handled with QPointF.
Using the above, if you are going to send point information use QPointF instead of int, int in the signals.
Use the methods that Qt provides, for example the following:
if (0 <= clickPosition.x() and clickPosition.x() <= GRID_SIDE*WIDTH and
0 <= clickPosition.y() and clickPosition.y() <= GRID_SIDE*HEIGHT)
It can be reduced to the following:
if(sceneRect().contains(event->scenePos()))
The advantage of that implementation is that it is more readable
I do not understand why you can not implement collidingItems, maybe it's the configuration of your .pro,
Use the following to add the gui module, core and widgets
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
Also to implement the animation use my previous answer
The complete and functional code can be found at the following link.
I am attempting to lower the font size of a TLabel if its text is to large to fit in the confines of the label. I didn't see any properties I could set on the label to achieve this, so I have tried writing my own method. My method works by using TCanvas.TextWidth to measure the width of the text in a label, and shrink the font until the width of the text fits within the width of the label.
void __fastcall ShrinkFontToFitLabel( TCanvas * Canvas, TLabel * Label )
{
float NewFontSize = Label->Font->Size;
Canvas->Font->Family = Label->Font->Family;
Canvas->Font->Size = NewFontSize;
while( Canvas->TextWidth( Label->Text ) > Label->Width && NewFontSize > MinimumFontSize )
{
NewFontSize -= FontSizeDecrement;
Canvas->Font->Size = NewFontSize;
}
Label->Font->Size = NewFontSize;
}
This works some of the time, however other times it does not shrink the font near enough. It seems as if the value I get from calling Canvas->TextWidth is a lot of times, much smaller than the number of pixels wide the label actually needs to be in order to fit the text.
Am I using Canvas->TextWidth incorrectly? Is there a better way to calculate the width of a string, or to re-size the font of a TLabel so its text fits within its demensions?
Edit:
In this case, I am passing in to my function, the TCanvas that my label is sitting in. I have tried using that TCanvas as well as Label->Canvas. Both give me the same number for text width, and both are short of the actual value in pixels needed to display the whole string.
The following code is taken from code that works in an FMX application, modified slightly to remove arrays that are being iterated through and declaring a variable locally to the function. It is being run in a TForm method. Canvas here is the Form's Canvas. You can see that I'm using "- 35" at one point - this might be because the numbers weren't quite right.
double InitialFontSize = 30;
Canvas->Font->Size = InitialFontSize;
StoryHeadlineLabel->Font->Size = InitialFontSize;
bool fits = false;
do
{
double widthA = Canvas->TextWidth (StoryHeadlineLabel->Text);
if (widthA > StoryHeadlineLabel->Width - 35)
{
StoryHeadlineLabel->Font->Size --;
Canvas->Font->Size --;
}
else
fits = true;
if (StoryHeadlineLabel->Font->Size < 6)
fits = true;
} while (!fits);
I have a dialog with a tree view inside it, and would like to have the dialog re-size itself automatically when the tree is expanded or collapsed to avoid scroll bars or excessive space.
In order to do so I need some way of finding the "desired" size of the tree view, i.e., the smallest size large enough to avoid displaying scroll bars.
Any suggestions?
Edit: So, I'm halfway there. I can determine the height by counting the number of visible items and multiplying by TreeView_GetItemHeight. I still have no idea how to find the width, however...
It's not quite perfect (it doesn't seem possible to have TreeView_GetItemRect horizontally include the whole line up to the end of text), but the following works great for my use case with disabled horizontal scrolling.
void Dialog::getDimensionTreeView(unsigned int id,
unsigned int &width, unsigned int &height) {
HWND item = GetDlgItem((HWND)_hwnd, id);
if(!item) {
width = 0;
height = 0;
return;
}
RECT area = { };
HTREEITEM node = TreeView_GetRoot(item);
do {
RECT rc;
LPRECT prc = &rc;
// Ideally this would use `fItemRect`=FALSE, but that seems
// to just return the current width of the treeview control.
TreeView_GetItemRect(item, node, prc, TRUE);
if(rc.left < area.left) area.left = rc.left;
if(rc.right > area.right) area.right = rc.right;
if(rc.top < area.top) area.top = rc.top;
if(rc.bottom > area.bottom) area.bottom = rc.bottom;
} while((node = TreeView_GetNextVisible(item, node)));
width = area.right - area.left;
height = area.bottom - area.top;
}
Thanks to Hans Passant for putting me on the right track.
I am coding a small map editor (with rectangle tiles) and I need a way to draw a large amount of images OR one big image. The application is simple: You draw images on an empty screen with your mouse and when you are finished you can save it. A tile consists of a small image.
I tried out several solutions to display the tiles:
Each tile has its own QGraphicsItem (This works until you have a
1000x1000 map)
Each tile gets drawn on one big QPixmap (This means a very large image. Example: Map with 1000x100 and each tile has a size of 32x32 means that the QPixmap has a size of 32000x32000. This is a problem for QPainter.)
The current solution: Iterate through width & height of the TileLayer and draw each single tile with painter->drawPixmap(). The paint() method of my TileLayer looks like this:
void TileLayerGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option,QWidget* /*widget*/)
{
painter->setClipRect(option->exposedRect);
int m_width=m_layer->getSize().width();
int m_height=m_layer->getSize().height();
for(int i=0;i<m_width;i++)
{
for(int j=0;j<(m_height);j++)
{
Tile* thetile=m_layer->getTile(i,j);
if(thetile==NULL)continue;
const QRectF target(thetile->getLayerPos().x()*thetile->getSize().width(),thetile->getLayerPos().y()*thetile->getSize().height(),thetile->getSize().width(),thetile->getSize().height());
const QRectF source(0, 0, thetile->getSize().width(), thetile->getSize().height());
painter->drawImage(target,*thetile->getImage(),source);
}
}}
This works for small maps with 100x100 or even 1000x100 tiles. But not for 1000x1000. The whole application begins to lag, this is of course because I have a for loop that is extremely expensive. To make my tool useful I need to be able to make at least 1000x1000 tilemaps without lags. Does anyone have an idea what I can do? How should I represent the tiles?
Update:
I changed the following: Only maps that exceed the window size of the minimap will be drawn with drawing single pixels for each tile. This is my render function now:
void RectangleRenderer::renderMinimapImage(QPainter* painter, TileMap* map,QSize windowSize)
{
for(int i=0;i<map->getLayers().size();i++)
{
TileLayer* currLayer=map->getLayers().at(i);
//if the layer is small draw it completly
if(windowSize.width()>currLayer->getSize().width()&&windowSize.height()>currLayer->getSize().height())
{
...
}
else // This is the part where the map is so big that only some pixels are drawn!
{
painter->fillRect(0,0,windowSize.width(),windowSize.height(),QBrush(QColor(map->MapColor)));
for(float i=0;i<windowSize.width();i++)
for(float j=0;j<windowSize.height();j++)
{
float tX=i/windowSize.width();
float tY=j/windowSize.height();
float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);
Tile* thetile=currLayer->getTile((int)pX,(int)pY);
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(i,j);
}
}
}
}
This works not correct, however it is pretty fast. The problem is my lerp(linear interpolation) function to get the correct tiles to draw a pixel from.
Does anyone have a better solution to get the correct tiles while I iterate through the minimap pixels? At the moment I use linear interpolation between 0 and the maximum size of the tilemap and it does not work correctly.
UPDATE 2
//currLayer->getSize() returns how many tiles are in the map
// currLayer->getTileSize() returns how big each tile is (32 pixels width for example)
int raw_width = currLayer->getSize().width()*currLayer->getTileSize().width();
int raw_height = currLayer->getSize().height()*currLayer->getTileSize().height();
int desired_width = windowSize.width();
int desired_height = windowSize.height();
int calculated_width = 0;
int calculated_height = 0;
// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;
// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width;
qreal ratio_y = (qreal)desired_height / raw_height;
qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2,0);
}
for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
//trying to do the following: use your code to get the desired pixel. Then divide that number by the size of the tile to get the correct pixel
Tile* thetile=currLayer->getTile((int)((r * floating_factor)*raw_width)/currLayer->getTileSize().width(),(int)(((c * floating_factor)*raw_height)/currLayer->getTileSize().height()));
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(r,c);
}
}
Trying to reverse engineer the example code, but it still does not work correctly.
Update 3
I tried (update 1) with linear interpolation again. And while I looked at the code I saw the error:
float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);
should be:
float pX=lerp(0,currLayer->getSize().width(),tX);
float pY=lerp(0,currLayer->getSize().height(),tY);
That's it. Now it works.
This shows how to do it properly. You use a level of detail (lod) variable to determine how to draw the elements that are currently visible on the screen, based on their zoom.
http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-example.html
Also don't iterate through all the elements that could be visible, but only go through the ones that have changed, and of those, only the ones that are currently visible.
Your next option to use is some other manual caching, so you don't have to repeatedly iterate through O(n^2) constantly.
If you can't optimize it for QGraphicsView/QGraphicsScene... then OpenGL is probably what you may want to look into. It can do a lot of the drawing and caching directly on the graphics card so you don't have to worry about it as much.
UPDATE:
Pushing changes to QImage on a worker thread can let you cache, and update a cache, while leaving the rest of your program responsive, and then you use a Queued connection to get back on the GUI thread to draw the QImage as a Pixmap.
QGraphicsView will let you know which tiles are visible if you ask nicely:
http://qt-project.org/doc/qt-5/qgraphicsview.html#items-5
UPDATE 2:
http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-chip-cpp.html
You may need to adjust the range of zooming out that is allowed on the project to test this feature...
Under where it has
const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
if (lod < 0.2) {
if (lod < 0.125) {
painter->fillRect(QRectF(0, 0, 110, 70), fillColor);
return;
}
QBrush b = painter->brush();
painter->setBrush(fillColor);
painter->drawRect(13, 13, 97, 57);
painter->setBrush(b);
return;
}
Add in something like:
if(lod < 0.05)
{
// using some sort of row/col value to know which ones to not draw...
// This below would only draw 1/3 of the rows and 1/3 of the column
// speeding up the redraw by about 9x.
if(row%3 != 0 || col%3 != 0)
return;// don't do any painting, return
}
UPDATE 3:
Decimation Example:
// How to decimate an image to any size, properly
// aka fast scaling
int raw_width = 1000;
int raw_height = 1000;
int desired_width = 300;
int desired_height = 200;
int calculated_width = 0;
int calculated_height = 0;
// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;
// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width();
qreal ratio_y = (qreal)desired_height / raw_height();
qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2);
}
for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
pixel[r][c] = raw_pixel[(int)(r * floating_factor)*raw_width][(int)(c * floating_factor)];
}
}
Hope that helps.
I am using a custom font called KomikaTitle. In some cases the font appears cut off on the left in the first character. This doesn't happen when I use a native font such as Arial.
The following is the code I am using:
scoreDisplayLabel = [CCLabelTTF labelWithString:#"0" dimensions:CGSizeMake(200,30) hAlignment:UITextAlignmentLeft fontName:#"KomikaTitle" fontSize:18];
scoreDisplayLabel.color = (ccc3(r,b,g));
[self addChild:scoreDisplayLabel z:2];
[scoreDisplayLabel setPosition:ccp(115,wins.height-73)];
How do I prevent this from happening? I am attaching a screenshot of the issue.
I tried messing around as suggested in http://www.cocos2d-iphone.org/forums/topic/custom-font-being-cut-off/, but no luck.
Thanks guys!
This maybe isn't a real answer, but I had the same problem with that font in an old cocos2d project I made. Just just added an extra space and a row.
This may or may not be related, but according to this source you have to include the file extension of your font. Where you have
fontName:#"KomikaTitle"
it should be
fontName:#"KomikaTitle.ttf"
for example.
If there are any android users out there using cocos2dx, this is not necessarily an easy problem to solve, but it is doable once you go down the rabbit hole. It does require editing the Cocos2dxBitmap.java file, which means that any changes made could be overrided by an update. Basically, the methods that are used to measure text are, while not incorrect, inadequate.
First, we need to add a new variable to the TextProperty
private final int mX;
Next, replace the computeTextProperty code with the following:
private static TextProperty computeTextProperty(final String pString, final int unusedWidth, final int unusedHeight, final Paint pPaint) {
final FontMetricsInt fm = pPaint.getFontMetricsInt();
final int h = (int) Math.ceil(fm.bottom - fm.top);
int maxContentWidth = 0;
final String[] lines = Cocos2dxBitmap.splitString(pString, 0,
0, pPaint);
/* Compute the max width. */
int temp = 0;
float left = 0;
for (final String line : lines) {
//get a path from text
Path path = new Path();
pPaint.getTextPath(line, 0, line.length(), 0, 0, path);
RectF bounds = new RectF();
path.computeBounds(bounds, true);
temp = (int) FloatMath.ceil(bounds.width());
//if the text extends to the left of 0
if (bounds.left < left) {
left = bounds.left;
}
if (temp > maxContentWidth) {
maxContentWidth = temp;
//extend the width to account for text rendered to the left of 0
if (left < bounds.left) {
maxContentWidth += (int) FloatMath.ceil(Math.abs(left));
}
}
}
left = Math.abs(left);
return new TextProperty(maxContentWidth, h, lines, (int) FloatMath.ceil(left));
}
What has basically happened is that we have used information returned by the text path to get if the left bound is less than 0, which would mean it would be rendered outside the bitmap. We also extend the width when there are multiple lines of text, as we are going to shift everything to match the left bounds, we need the right bounds shifted too.
Finally, replace computeX with
private static int computeX(final String pText, final int pMaxWidth,
final int pHorizontalAlignment, final int pX) {
int ret = 0;
int expectedWidth = pX + pMaxWidth;
switch (pHorizontalAlignment) {
case HORIZONTALALIGN_CENTER:
ret = expectedWidth / 2;
break;
case HORIZONTALALIGN_RIGHT:
ret = expectedWidth;
break;
case HORIZONTALALIGN_LEFT:
ret = pX;
default:
break;
}
return ret;
}
You'll have to do all the hookups yourself, but this will provide the most accurate text rendering.