您的位置:首页 > 其它

CCImage(读取加载图片)

2014-05-23 10:36 190 查看
http://www.2cto.com/kf/201210/158630.html

Cocos2d-x之CCImage深入分析

CCImage类:支持从JPG,PNG,TIFF以及数据流,字符串中创建供Cocos2d-x进行访问的图片数据对象。

这是一个非常重要的类,因为它是你使用cocos2d-x来加载图片的基础。目前此类只支持JPG,PNG,TIFF三种图像文件,对于这三种图像文件,CCImage分别使用libjpg,libpng,libtiff进行读取访问和写文件的操作。有兴趣的同学可以上网查询相关图像库的分析和使用,同时建议有能力的同学对此类进行扩展,令它支持更多的图像格式,是为“举一反三”。

学之前最好先了解一下libjpg,libpng,libtiff等图片功能库的使用。可以参见文章底部的参考资料。

现在,先来看CCImage图像头文件:

.h

#ifndef __CC_IMAGE_H__

#define __CC_IMAGE_H__

#include "cocoa/CCObject.h"

NS_CC_BEGIN

class CC_DLL CCImage : public CCObject

{

public:

CCImage();

~CCImage();

//支持的图片类型

typedef enum

{

kFmtJpg = 0, //JPG

kFmtPng, //PNG

kFmtTiff, //TIFF

kFmtRawData, //数据流,要求为RGBA8888

kFmtUnKnown //无效

}EImageFormat;

//对齐方式

typedef enum

{

kAlignCenter = 0x33, //横向纵向都居中.

kAlignTop = 0x13, //横向居中,纵向居上.

kAlignTopRight = 0x12, //横向居右,纵向居上.

kAlignRight = 0x32, //横向居中,纵向居中.

kAlignBottomRight = 0x22, //横向居右,纵向居下.

kAlignBottom = 0x23, //横向居中,纵向居下.

kAlignBottomLeft = 0x21, //横向居左,纵向居下.

kAlignLeft = 0x31, //横向居左,纵向居中.

kAlignTopLeft = 0x11, //横向居左,纵向居上.

}ETextAlign;

//从指定的路径载入一个所支持的格式的图片文件。

bool initWithImageFile(const char * strPath, EImageFormat imageType = kFmtPng);

//从指定的路径载入一个所支持的格式的图片文件,但它是线程安全的,因此可以用在多线程加载图片。

bool initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType = kFmtPng);

//从内存中加载图片数据。

bool initWithImageData(void * pData, //指向图片数据所处内存地址的指针。

int nDataLen, //图片数据长度

EImageFormat eFmt = kFmtUnKnown, //数据对应图片的格式,

int nWidth = 0, //数据对应图片的宽度

int nHeight = 0, //数据对应图片的高度

int nBitsPerComponent = 8); //每像素的字节位数,即色深。

//从字符串创建图片数据。

bool initWithString(

const char * pText,
//字符串

int nWidth = 0, //要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。

int nHeight = 0, //要创建的图片高度,如果填0,则按照字符串的高度进行设置。

ETextAlign eAlignMask = kAlignCenter, //文字的对齐方式。

const char * pFontName = 0, //字体名称

int nSize = 0); //字体大小

//取得图像数据地址

unsigned char * getData() { return m_pData; }

//取得图像数据的长度

int getDataLen() { return m_nWidth * m_nHeight; }

//是否有Alpha通道。

bool hasAlpha() { return m_bHasAlpha; }

//是否有Alpha渐变

bool isPremultipliedAlpha() { return m_bPreMulti; }

bool saveToFile(const char *pszFilePath, bool bIsToRGB = true);
//
将当前图片数据保存成指定的文件格式。 1、绝对路径名 2、是否保存成RGB格式

//定义属性

CC_SYNTHESIZE_READONLY(unsigned short, m_nWidth, Width); //定义变量m_nWidth和get接口

CC_SYNTHESIZE_READONLY(unsigned short, m_nHeight, Height); //定义变量m_nHeight和get接口

CC_SYNTHESIZE_READONLY(int, m_nBitsPerComponent, BitsPerComponent);
//定义变量m_nBitsPerComponent和get接口

protected:

//读取JPG图片数据 1:数据地址 2:数据长度

bool _initWithJpgData(void *pData, int nDatalen);

//读取PNG图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中

bool _initWithPngData(void *pData, int nDatalen);

//读取TIFF图片数据到内存成成Cocos2d-x所用的图像数据保存到m_pData中

bool _initWithTiffData(void* pData, int nDataLen);

//读取RGBA8888格式的图片数据。 1:数据地址 2:数据长度 3:图片宽度 4:图片高度 5:图片色深

bool _initWithRawData(void *pData, int nDatalen, int nWidth, int nHeight, int nBitsPerComponent);

//将图像数据保存为PNG图片

bool _saveImageToPNG(const char *pszFilePath, bool bIsToRGB = true);

//将图像数据保存为JPG图片

bool _saveImageToJPG(const char *pszFilePath);

//图像数据地址

unsigned char *m_pData;

//是否有Alpha

bool m_bHasAlpha;

//是否有Alpha渐变

bool m_bPreMulti;

private:

// 拷贝构造与重载等号拷贝

CCImage(const CCImage& rImg);

CCImage & operator=(const CCImage&);

};

NS_CC_END

#endif

继续分析CPP文件,其CPP文件由两个文件构成:

CCImageCommon_cpp.h和CCImage.cpp。

//可以理解为基类 子类 每个平台都有其对应的CCImage.cpp 文件 并且include CCImageCommon_cpp.h
使用CCImageCommon_cpp.h封装出接口

CCImageCommon_cpp.h 见->cocos2dx/platform->CCImageCommon_cpp (写一起太长了)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.cpp //win32平台下CCImage.cpp为例子

#define __CC_PLATFORM_IMAGE_CPP__

#include "platform/CCImageCommon_cpp.h"

NS_CC_BEGIN

//此处定义一个BitmapDC类在位图上进行文字绘制。

class BitmapDC

{

public:

//构造函数

BitmapDC(HWND hWnd = NULL)

: m_hDC(NULL)

, m_hBmp(NULL)

, m_hFont((HFONT)GetStockObject(DEFAULT_GUI_FONT))

, m_hWnd(NULL)

{

//保存窗口句柄

m_hWnd = hWnd;

//取得窗口的hdc

HDC hdc = GetDC(hWnd);

//创建兼容的hdc

m_hDC = CreateCompatibleDC(hdc);

//释放hdc

ReleaseDC(hWnd, hdc);

}

//析构函数

~BitmapDC()

{

prepareBitmap(0, 0);

if (m_hDC)

{

DeleteDC(m_hDC);

}

//创建字体

HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);

if (hDefFont != m_hFont)

{

DeleteObject(m_hFont);

m_hFont = hDefFont;

}

// release temp font resource

if (m_curFontPath.size() > 0)

{

wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);

if (pwszBuffer)

{

RemoveFontResource(pwszBuffer);

SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);

delete [] pwszBuffer;

pwszBuffer = NULL;

}

}

}

//多字节转宽字符

wchar_t * utf8ToUtf16(std::string nString)

{

wchar_t * pwszBuffer = NULL;

do

{

if (nString.size() < 0)

{

break;

}

// utf-8 to utf-16

int nLen = nString.size();

int nBufLen = nLen + 1;

pwszBuffer = new wchar_t[nBufLen];

CC_BREAK_IF(! pwszBuffer);

memset(pwszBuffer,0,nBufLen);

nLen = MultiByteToWideChar(CP_UTF8, 0, nString.c_str(), nLen, pwszBuffer, nBufLen);

pwszBuffer[nLen] = '\0';

} while (0);

return pwszBuffer;

}

//设置使用的字体和大小

bool setFont(const char * pFontName = NULL, int nSize = 0)

{

bool bRet = false;

do

{

//创建字体

std::string fontName = pFontName;

std::string fontPath;

HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);

LOGFONTA tNewFont = {0};

LOGFONTA tOldFont = {0};

GetObjectA(hDefFont, sizeof(tNewFont), &tNewFont);

if (fontName.c_str())

{

// 如果字体名称是ttf文件,取得其全路径名

int nFindttf = fontName.find(".ttf");

int nFindTTF = fontName.find(".TTF");

if (nFindttf >= 0 || nFindTTF >= 0)

{

fontPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(fontName.c_str());

int nFindPos = fontName.rfind("/");

fontName = &fontName[nFindPos+1];

nFindPos = fontName.rfind(".");

fontName = fontName.substr(0,nFindPos);

}

tNewFont.lfCharSet = DEFAULT_CHARSET;

strcpy_s(tNewFont.lfFaceName, LF_FACESIZE, fontName.c_str());

}

if (nSize)

{

tNewFont.lfHeight = -nSize;

}

//

GetObjectA(m_hFont, sizeof(tOldFont), &tOldFont);

if (tOldFont.lfHeight == tNewFont.lfHeight

&& ! strcpy(tOldFont.lfFaceName, tNewFont.lfFaceName))

{

// already has the font

bRet = true;

break;

}

// 删除旧的字体

if (m_hFont != hDefFont)

{

DeleteObject(m_hFont);

// release old font register

if (m_curFontPath.size() > 0)

{

wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);

if (pwszBuffer)

{

if(RemoveFontResource(pwszBuffer))

{

SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);

}

delete [] pwszBuffer;

pwszBuffer = NULL;

}

}

fontPath.size()>0?(m_curFontPath = fontPath):(m_curFontPath.clear());

if (m_curFontPath.size() > 0)

{

wchar_t * pwszBuffer = utf8ToUtf16(m_curFontPath);

if (pwszBuffer)

{

if(AddFontResource(pwszBuffer))

{

SendMessage( m_hWnd, WM_FONTCHANGE, 0, 0);

}

delete [] pwszBuffer;

pwszBuffer = NULL;

}

}

}

m_hFont = NULL;

// 字体不使用锐利字体

tNewFont.lfQuality = ANTIALIASED_QUALITY;

// 创建新字体

m_hFont = CreateFontIndirectA(&tNewFont);

if (! m_hFont)

{

// create failed, use default font

m_hFont = hDefFont;

break;

}

bRet = true;

} while (0);

return bRet;

}

//取得使用当前字体写出的字符串所占据的图像大小。

SIZE sizeWithText(const wchar_t * pszText, int nLen, DWORD dwFmt, LONG nWidthLimit)

{

SIZE tRet = {0};

do

{

CC_BREAK_IF(! pszText || nLen <= 0);

RECT rc = {0, 0, 0, 0};

DWORD dwCalcFmt = DT_CALCRECT;

if (nWidthLimit > 0)

{

rc.right = nWidthLimit;

dwCalcFmt |= DT_WORDBREAK

| (dwFmt & DT_CENTER)

| (dwFmt & DT_RIGHT);

}

// 使用当前字体。

HGDIOBJ hOld = SelectObject(m_hDC, m_hFont);

// 写字。

DrawTextW(m_hDC, pszText, nLen, &rc, dwCalcFmt);

SelectObject(m_hDC, hOld);

tRet.cx = rc.right;

tRet.cy = rc.bottom;

} while (0);

return tRet;

}

//创建一个指定大小的位图。

bool prepareBitmap(int nWidth, int nHeight)

{

// 释放原来的位图

if (m_hBmp)

{

DeleteObject(m_hBmp);

m_hBmp = NULL;

} //如果大小有效则创建位图。

if (nWidth > 0 && nHeight > 0)

{

m_hBmp = CreateBitmap(nWidth, nHeight, 1, 32, NULL);

if (! m_hBmp)

{

return false;

}

}

return true;

}

//在指定位置按照指定对齐方式写字。 1:字符串 2:指定拉置及大小 3:文字对齐方式

int drawText(const char * pszText, SIZE& tSize, CCImage::ETextAlign eAlign)

{

int nRet = 0;

wchar_t * pwszBuffer = 0;

do

{

CC_BREAK_IF(! pszText);

DWORD dwFmt = DT_WORDBREAK;

DWORD dwHoriFlag = eAlign & 0x0f;

DWORD dwVertFlag = (eAlign & 0xf0) >> 4;

//设置横向对齐方式。

switch (dwHoriFlag)

{

case 1: // 左对齐

dwFmt |= DT_LEFT;

break;

case 2: // 右对齐

dwFmt |= DT_RIGHT;

break;

case 3: // 居中

dwFmt |= DT_CENTER;

break;

}

//取得字符串的字节长度。

int nLen = strlen(pszText);

int nBufLen = nLen + 1;

// 为转换后的宽字符串申请内存地址。

pwszBuffer = new wchar_t[nBufLen];

CC_BREAK_IF(! pwszBuffer);

//内存数据置零

memset(pwszBuffer, 0, sizeof(wchar_t)*nBufLen);

// 将多字节转宽字符串。

nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen);

//取得写字符串占据的图像区域大小

SIZE newSize = sizeWithText(pwszBuffer, nLen, dwFmt, tSize.cx);

//建立RECT变量做为实际绘制占据的图像区域大小

RECT rcText = {0};

// 如果宽度为0,则使用显示字符串所需的图像大小。

if (tSize.cx <= 0)

{

tSize = newSize;

rcText.right = newSize.cx;

rcText.bottom = newSize.cy;

}

else

{

LONG offsetX = 0;

LONG offsetY = 0;

rcText.right = newSize.cx;

//根据对齐方式计算横向偏移。

if (1 != dwHoriFlag

&& newSize.cx < tSize.cx)

{

offsetX = (2 == dwHoriFlag) ? tSize.cx - newSize.cx : (tSize.cx - newSize.cx) / 2; }

//如果指定矩形高度为0,则使用显示字符串所需的图像高度。

if (tSize.cy <= 0)

{

tSize.cy = newSize.cy;

dwFmt |= DT_NOCLIP;

rcText.bottom = newSize.cy; // store the text height to rectangle

}

else if (tSize.cy < newSize.cy)

{

// content height larger than text height need, clip text to rect

rcText.bottom = tSize.cy;

}

else

{

rcText.bottom = newSize.cy;

// content larger than text, need adjust vertical position

dwFmt |= DT_NOCLIP;

//根据对齐方式计算纵向偏移。

offsetY = (2 == dwVertFlag) ? tSize.cy - newSize.cy // 居下 : (3 == dwVertFlag) ? (tSize.cy - newSize.cy) / 2 // 居中 : 0;
// 居上 }

//如果需要,调整偏移。

if (offsetX || offsetY)

{

OffsetRect(&rcText, offsetX, offsetY);

}

}

//创建相应大小的位图。

CC_BREAK_IF(! prepareBitmap(tSize.cx, tSize.cy));

// 使用当前字体和位图

HGDIOBJ hOldFont = SelectObject(m_hDC, m_hFont);

HGDIOBJ hOldBmp = SelectObject(m_hDC, m_hBmp);

//设置背景透明模式和写字的颜色

SetBkMode(m_hDC, TRANSPARENT);

SetTextColor(m_hDC, RGB(255, 255, 255)); // white color

//写字

nRet = DrawTextW(m_hDC, pwszBuffer, nLen, &rcText, dwFmt);

//DrawTextA(m_hDC, pszText, nLen, &rcText, dwFmt);

//还原为之前使用的字体和位图

SelectObject(m_hDC, hOldBmp);

SelectObject(m_hDC, hOldFont);

} while (0);

//释放内存

CC_SAFE_DELETE_ARRAY(pwszBuffer);

return nRet;

}

//成员变量m_hDC及get接口

CC_SYNTHESIZE_READONLY(HDC, m_hDC, DC);

//成员变量m_hBmp及get接口

CC_SYNTHESIZE_READONLY(HBITMAP, m_hBmp, Bitmap);

private:

//友元类CCImage

friend class CCImage;

//成员变量m_hFont代表字体

HFONT m_hFont;

//成员变量m_hWnd代表当前窗口句柄。

HWND m_hWnd;

//成员m_curFontPath代表当前字体ttf文件全路径。

std::string m_curFontPath;

};

//取得单例BitmapDC

static BitmapDC& sharedBitmapDC()

{

static BitmapDC s_BmpDC;

return s_BmpDC;

}

//CCImage的成员函数,使用相应的字体写字符串生成图像数据。

bool CCImage::initWithString(

const char * pText, //1:字符串

int nWidth/* = 0*/, //2:要创建的图片宽度,如果填0,则按照字符串的宽度进行设置。

int nHeight/* = 0*/, //3:要创建的图片高度,如果填0,则按照字符串的高度进行设置。

ETextAlign eAlignMask/* = kAlignCenter*/, //4:文字的对齐方式。

const char * pFontName/* = nil*/, //5:字体名称

int nSize/* = 0*/) //6:字体大小

{

bool bRet = false;

unsigned char * pImageData = 0;

do

{

CC_BREAK_IF(! pText);

//取得单例BitmapDC

BitmapDC& dc = sharedBitmapDC();

//设置使用的字体和大小

if (! dc.setFont(pFontName, nSize))

{

CCLog("Can't found font(%s), use system default", pFontName);

}

// 写字

SIZE size = {nWidth, nHeight};

CC_BREAK_IF(! dc.drawText(pText, size, eAlignMask));

//申请图像大小的内存,成功申请后将其地址返回给指针pImageData。

pImageData = new unsigned char[size.cx * size.cy * 4];

CC_BREAK_IF(! pImageData);

//创建位图头信息结构

struct

{

BITMAPINFOHEADER bmiHeader;

int mask[4];

} bi = {0};

bi.bmiHeader.biSize = sizeof(bi.bmiHeader);

//取得写字符串的位图像素

CC_BREAK_IF(! GetDIBits(dc.getDC(), dc.getBitmap(), 0, 0,

NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS));

m_nWidth = (short)size.cx;

m_nHeight = (short)size.cy;

m_bHasAlpha = true;

m_bPreMulti = false;

m_pData = pImageData;

pImageData = 0;

m_nBitsPerComponent = 8;

// 位图像素拷到pImageData中。

bi.bmiHeader.biHeight = (bi.bmiHeader.biHeight > 0)

? - bi.bmiHeader.biHeight : bi.bmiHeader.biHeight;

GetDIBits(dc.getDC(), dc.getBitmap(), 0, m_nHeight, m_pData,

(LPBITMAPINFO)&bi, DIB_RGB_COLORS);

// 双循环遍历每个像素。取得R,G,B值组成4字节的RGBA值保存。

COLORREF * pPixel = NULL;

for (int y = 0; y < m_nHeight; ++y)

{

pPixel = (COLORREF *)m_pData + y * m_nWidth;

for (int x = 0; x < m_nWidth; ++x)

{

COLORREF& clr = *pPixel;

if (GetRValue(clr) || GetGValue(clr) || GetBValue(clr))

{

clr |= 0xff000000;

}

++pPixel;

}

}

bRet = true;

} while (0);

return bRet;

}

NS_CC_END

CCImage类的代码都解析完了,其内部并没有对png,jpg,tiff做真正的解析,只是利用第三方的库进行相应的处理。经过处理后,CCImage会被图片文件的数据读取转换为Cocos2d-x所用的图像数据,存储在m_pData中。如果我们不是使用这些图片文件,那么我们就需要自已增加接口函数进行相应的处理。比如有时候做游戏不希望使用标准的图像格式以防止别人能够很容易打开图片进行查看,或者有时候将多个图片组成一个数据包。

例如:

我们想读取一个自定义二进制图片数据(假设为Pic格式)

(1)我们可以先定义一个新的图片类型kFmtPic,

(2)然后定义函数

bool _initWithPicData(void *pData, int nDatalen);

在其中将自定义图片文件数据pData解析到m_pData中,并设置一些属性变量。

(3) 然后在initWithImageData函数中加入:

[cpp]

else if (kFmtPic == eFmt)

//读取自定义的图片数据。

bRet = _initWithPicData(pData, nDataLen);

break;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: