Mixing retina and standard images

Working with UIKit shows how easy it is to mix both retina and standard images - you can just replace the most critical images with hi-res and leave the rest as they are.

However, it doesn’t work this way with cocos2d. As soon as you enable retina without replacing all your images with doubled resolution, you will see that all images are rendered in pixels but not points (twice smaller).

I think, as cocos2d has already incorporated the idea about using both points and pixels, it would make sense to follow the same principles with loading images:

  1. If there is HD version of image, then it’s size set in pixels
  2. If there is only standard version of image, then it’s size should be set in points.

In this case, if you have only standard resolution of your images, just by enabling retina you should at least see smoother fonts.

I was trying to do some research and it is already implemented in cocos2d 2.0 - http://www.cocos2d-iphone.org/forum/topic/22896

Do you think it would be possible to include this logic in cocos2dx? (I’m currently trying to do it as well, but it goes not very smoothly)

Cool, what problem did you meet?

The problem is that if you don’t put HD images and enable retina, then those images are treated as HD (settings size of CCSprite in pixels but not points) and the game becomes very funny - all positions of objects are correct, but images are twice smaller.

I’ve almost solved it now. There are still problems is flipping sprites. I will post my changes here as soon as I finish

If you understood the problem, let my try to explain how I’m trying to solve it (idea is the same as in the link above). However, I can’t resolve positioning with different anchor points and flipX/Y.

First of all, we need is to know weather current texture is HD or standard.

So in CCTexture2D.h putting:

typedef enum {
    kCCResolutionStandardDisplay,
    kCCResolutionRetinaDisplay
} ccResolutionType;

and in the class declaration:

CC_SYNTHESIZE(ccResolutionType, m_resolutionType, ResolutionType);

in CCTexture2D.cpp adding initialisation to the constructor:

CCTexture2D::CCTexture2D()
: m_uPixelsWide(0)
, m_uPixelsHigh(0)
, m_uName(0)
, m_fMaxS(0.0)
, m_fMaxT(0.0)
, m_bHasPremultipliedAlpha(false)
, m_bPVRHaveAlphaPremultiplied(true)
, m_resolutionType( kCCResolutionRetinaDisplay )

{
}

I put Retina quality by default, because if display doesn’t have retina display then in the CCSprite code it will be just ignored.

Now to set this property, I changed CCTextureCache::addPVRImage (should be done in other methods as well, but I just have PVR textures, so the whole thing is just a test for me).

after line:

    tex = new CCTexture2D();

Adding this:

    // Identifying resolution type
    if (fullpath.length() == CCFileUtils::ccRemoveHDSuffixFromFile(fullpath).length()) {
        tex->setResolutionType(kCCResolutionStandardDisplay);
    }

As labels use textures as well, I put the following code in the CCLabelTTF:setString

if (CC_CONTENT_SCALE_FACTOR() > 1)
        texture->setResolutionType(kCCResolutionRetinaDisplay);

just before:

this->setTexture(texture);

Again, this probably should be done for other Labels too, but for testing purposes I modified only CCLabelTTF, that I’m using in my project.

Next thing is modifications of CCSprite.

First of all, I added property in file CCSprite.h:

    bool m_bIsHD;

and then in method init() put line:

    m_bIsHD = CC_CONTENT_SCALE_FACTOR() > 1;

and in method CCSprite::initWithTexture(CCTexture2D **pTexture, const CCRect& rect)
after setTexture added:
<pre>
m_bIsHD = == kCCResolutionRetinaDisplay);
</pre>
And then all chaos is in the method CCSprite::setTextureRectInPixels. I’m just going to copy the beginning of the code:
<pre>
void CCSprite::setTextureRectInPixels
{
m_obRectInPixels = rect;
m_obRect = CC_RECT_PIXELS_TO_POINTS;
m_bRectRotated = rotated;

updateTextureCoords;
CCPoint relativeOffsetInPixels = m_obUnflippedOffsetPositionFromCenter;
if > 1) {
m_obRectInPixels = CC_RECT_POINTS_TO_PIXELS;
m_obRect = rect;
setContentSize;
relativeOffsetInPixels.x**= 2;
relativeOffsetInPixels.y *= 2;
} else {
setContentSizeInPixels(size);
}

// issue #732
if (m_bFlipX)
{
relativeOffsetInPixels.x = ~~relativeOffsetInPixels.x;
}
if
{
relativeOffsetInPixels.y =~~relativeOffsetInPixels.y;
}

Also to fix flipX and flipY I modified these methods:
void CCSprite::setFlipX(bool bFlipX)
{
    if (m_bFlipX != bFlipX)
    {
        m_bFlipX = bFlipX;

        if (!m_bIsHD && CC_CONTENT_SCALE_FACTOR() > 1) {
            setTextureRectInPixels(CC_RECT_PIXELS_TO_POINTS(m_obRectInPixels), m_bRectRotated, m_tContentSizeInPixels);
        } else {
            setTextureRectInPixels(m_obRectInPixels, m_bRectRotated, m_tContentSizeInPixels);
        }
    }
}

void CCSprite::setFlipY(bool bFlipY)
{
    if (m_bFlipY != bFlipY)
    {
        m_bFlipY = bFlipY;
        if (!m_bIsHD && CC_CONTENT_SCALE_FACTOR() > 1) {
            setTextureRectInPixels(CC_RECT_PIXELS_TO_POINTS(m_obRectInPixels), m_bRectRotated, m_tContentSizeInPixels);
        } else {
            setTextureRectInPixels(m_obRectInPixels, m_bRectRotated, m_tContentSizeInPixels);
        }
    }
}

These are all the changes. Now almost everything works perfectly, apart from images, where I put setFlipX(true), setFlipY(true) and setAnchorPoint( ccp(1, 1) ). The image just displays at the wrong place. I’m already a bit confused and all these changes looks a bit spaghetti to me :slight_smile: Hopefully, this might be a start for somebody who wants to resolve the same issue and hopefully, it will be shared with others too :slight_smile: