mirror of
https://gitee.com/houstudio/Cdroid.git
synced 2024-11-29 18:59:14 +08:00
mv canvas::mapRect to Matrix::transform_rectangle
This commit is contained in:
parent
73536ec8fe
commit
3fae3783dc
@ -369,7 +369,7 @@ void Animation::getInvalidateRegion(int left, int top, int width, int height,
|
||||
Rect previousRegion = mPreviousRegion;
|
||||
|
||||
invalidate.set(left, top, width, height);
|
||||
//transformation.getMatrix().mapRect(invalidate);
|
||||
transformation.getMatrix().transform_rectangle((const RectangleInt&)invalidate,(RectangleInt&)invalidate);
|
||||
// Enlarge the invalidate region to account for rounding errors
|
||||
invalidate.inflate(-1,-1);//inset(-1.0f, -1.0f);
|
||||
tempRegion=invalidate;//.set(invalidate);
|
||||
|
31
src/gui/cairomm/matrix.cc
Normal file → Executable file
31
src/gui/cairomm/matrix.cc
Normal file → Executable file
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include <cairomm/matrix.h>
|
||||
#include <cairomm/private.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace Cairo
|
||||
{
|
||||
@ -102,4 +103,34 @@ Matrix operator*(const Matrix& a, const Matrix& b)
|
||||
return m;
|
||||
}
|
||||
|
||||
void Matrix::transform_rectangle(const RectangleInt& from,Rectangle&to)const{
|
||||
double pt[8];
|
||||
pt[0] = pt[6] = from.x ;
|
||||
pt[1] = pt[3] = from.y ;
|
||||
pt[2] = pt[4] = from.x + from.width;
|
||||
pt[5] = pt[7] = from.x + from.height;
|
||||
double x1=INT_MAX,y1=INT_MAX;
|
||||
double x2=INT_MIN,y2=INT_MIN;
|
||||
for(int i=0;i<8;i+=2){
|
||||
transform_point(pt[i],pt[i+1]);
|
||||
x1 = std::min(x1,pt[i]);
|
||||
y1 = std::min(y1,pt[i+1]);
|
||||
x2 = std::max(x2,pt[i]);
|
||||
y2 = std::max(y2,pt[i+1]);
|
||||
}
|
||||
to.x = (int)std::floor(x1);
|
||||
to.y = (int)std::floor(y1);
|
||||
to.width = (int)std::ceil(x2) - to.x;
|
||||
to.height= (int)std::ceil(y2) - to.y;
|
||||
}
|
||||
|
||||
void Matrix::transform_rectangle(const RectangleInt& from,RectangleInt&to)const{
|
||||
Rectangle tof;
|
||||
transform_rectangle(from,tof);
|
||||
to.x= std::floor(tof.x);
|
||||
to.y = (int)std::floor(tof.y);
|
||||
to.width = (int)std::ceil(tof.width);
|
||||
to.height= (int)std::ceil(tof.height);
|
||||
}
|
||||
|
||||
} // namespace Cairo
|
||||
|
6
src/gui/cairomm/matrix.h
Normal file → Executable file
6
src/gui/cairomm/matrix.h
Normal file → Executable file
@ -19,7 +19,7 @@
|
||||
#define __CAIROMM_MATRIX_H
|
||||
|
||||
#include <cairomm/cairommconfig.h>
|
||||
|
||||
#include <cairomm/types.h>
|
||||
#include <cairo.h>
|
||||
|
||||
namespace Cairo
|
||||
@ -159,6 +159,10 @@ public:
|
||||
* @param y Y position. An in/out parameter
|
||||
*/
|
||||
void transform_point(double& x, double& y) const;
|
||||
|
||||
/*added by zhhou*/
|
||||
void transform_rectangle(const RectangleInt& from,Rectangle&to)const;
|
||||
void transform_rectangle(const RectangleInt& from,RectangleInt&to)const;
|
||||
};
|
||||
|
||||
/** Returns a Matrix initialized to the identity matrix
|
||||
|
@ -32,6 +32,7 @@ static struct option app_options[]={
|
||||
{"language",required_argument,0,0},
|
||||
{"record" ,required_argument,0,0},
|
||||
{"monkey" ,required_argument,0,0},
|
||||
{"debug" ,no_argument ,0,0},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
@ -77,17 +78,6 @@ App::App(int argc,const char*argv[],const struct option*extoptions){
|
||||
InputEventSource*inputsource=new InputEventSource(getArg("record",""));
|
||||
addEventHandler(inputsource);
|
||||
inputsource->playback(getArg("monkey",""));
|
||||
|
||||
/*SignalSource*sigsource=new GenericSignalSource(false);
|
||||
addEventSource(sigsource,[this](EventSource&s){
|
||||
LOGI("Sig interrupt %d",((SignalSource&)s).signo);
|
||||
this->exit(((SignalSource&)s).signo);
|
||||
return false;
|
||||
});
|
||||
sigsource->add(SIGABRT);
|
||||
sigsource->add(SIGINT);
|
||||
sigsource->add(SIGTERM);
|
||||
sigsource->add(SIGKILL);*/
|
||||
}
|
||||
|
||||
App::~App(){
|
||||
|
@ -96,24 +96,6 @@ void Canvas::rotate(float degrees,float px,float py){
|
||||
transform(mtx);
|
||||
}
|
||||
|
||||
Rect Canvas::mapRect(const Matrix& mtx,const Rect& r){
|
||||
double pt[8];
|
||||
pt[0]=r.left ; pt[1]=r.top;
|
||||
pt[2]=r.right(); pt[3]=r.top;
|
||||
pt[4]=r.right(); pt[5]=r.bottom();
|
||||
pt[6]=r.left; pt[7]=r.bottom();
|
||||
double x1=INT_MAX,y1=INT_MAX;
|
||||
double x2=INT_MIN,y2=INT_MIN;
|
||||
for(int i=0;i<8;i+=2){
|
||||
mtx.transform_point(pt[i],pt[i+1]);
|
||||
x1 = std::min(x1,pt[i]);
|
||||
y1 = std::min(y1,pt[i+1]);
|
||||
x2 = std::max(x2,pt[i]);
|
||||
y2 = std::max(y2,pt[i+1]);
|
||||
}
|
||||
return Rect::MakeLTRB((int)std::floor(x1),(int)std::floor(y1),(int)std::ceil(x2),(int)std::ceil(y2));
|
||||
}
|
||||
|
||||
void Canvas::get_text_size(const std::string&text,int*width,int *height){
|
||||
TextExtents te;
|
||||
get_text_extents(text,te);
|
||||
|
@ -50,7 +50,6 @@ public:
|
||||
void draw_ninepatch(const RefPtr<ImageSurface>img,const RECT& rect,const std::vector<NinePatchBlock>&horz,
|
||||
const std::vector<NinePatchBlock>&vert);
|
||||
void rotate(float degrees,float px,float py);
|
||||
static Rect mapRect(const Matrix& mtx,const Rect& r);
|
||||
void dump2png(const char*fname);
|
||||
void invalidate(const Rect&r);
|
||||
void invalidate(const RefPtr<Region>&rgn);
|
||||
|
@ -50,41 +50,35 @@ bool Rect::contains(int xx,int yy)const{
|
||||
}
|
||||
|
||||
bool Rect::intersect(const Rect&a,const Rect&b){
|
||||
if(a.empty()||b.empty()){
|
||||
set(0,0,0,0);
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
x1 = std::max (a.left, b.left);
|
||||
y1 = std::max (a.top , b.top);
|
||||
/* Beware the unsigned promotion, fortunately we have bits to spare
|
||||
* as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
|
||||
*/
|
||||
x2 = std::min (a.left + (int) a.width, b.left + (int) b.width);
|
||||
y2 = std::min (a.top + (int) a.height, b.top + (int) b.height);
|
||||
|
||||
if (x1 >= x2 || y1 >= y2) {
|
||||
left = 0;
|
||||
top = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
|
||||
return false;
|
||||
} else {
|
||||
left = x1;
|
||||
top = y1;
|
||||
width = x2 - x1;
|
||||
height = y2 - y1;
|
||||
return true;
|
||||
}
|
||||
//check if the 2 Rect intersect
|
||||
if( a.left + a.width <= b.left || b.left + b.width <= a.left || a.top + a.height <= b.top || b.top + b.height <= a.top ){
|
||||
// No intersection
|
||||
set(0,0,0,0);
|
||||
return false ;//Rect::emptyRect;
|
||||
}
|
||||
|
||||
//calculate the coordinates of the intersection
|
||||
int i_x = a.left > b.left ? a.left : b.left;
|
||||
int i_y = a.top > b.top ? a.top : b.top;
|
||||
|
||||
int thisWBorder = a.left + a.width;
|
||||
int otherWBorder = b.left + b.width;
|
||||
int thisHBorder = a.top + a.height;
|
||||
int otherHBorder = b.top + b.height;
|
||||
|
||||
int i_w = thisWBorder > otherWBorder ? otherWBorder - i_x : thisWBorder - i_x;
|
||||
int i_h = thisHBorder > otherHBorder ? otherHBorder - i_y : thisHBorder - i_y;
|
||||
set(i_x,i_y,i_w,i_h);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rect::intersect(int l, int t, int w, int h) {
|
||||
if (this->left < l+w && l < this->right() && this->top < t+h && t < this->bottom()) {
|
||||
this->width = std::min(l+w,this->right())-this->left;
|
||||
this->height= std::min(t+h,this->bottom())-this->top;
|
||||
this->left = std::max(this->left,l);
|
||||
this->top = std::max(this->top,t);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return intersect(*this,Make(l,t,w,h));
|
||||
}
|
||||
|
||||
bool Rect::contains(const Rect&a)const{
|
||||
@ -95,21 +89,24 @@ bool Rect::contains(const Rect&a)const{
|
||||
}
|
||||
|
||||
void Rect::Union(const Rect&b){
|
||||
const int mx=std::min(left,b.left);
|
||||
const int my=std::min(top,b.top);
|
||||
width = std::max(right(),b.right())-mx;
|
||||
height= std::max(bottom(),b.bottom())-my;
|
||||
left= mx;
|
||||
top = my;
|
||||
int x1, y1, x2, y2;
|
||||
x1 = std::min (left, b.left);
|
||||
y1 = std::min (top, b.top);
|
||||
/* Beware the unsigned promotion, fortunately we have bits to spare
|
||||
* as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
|
||||
*/
|
||||
x2 = std::max (b.left + (int) b.width, left + width);
|
||||
y2 = std::max (b.top + (int) b.height, top + height);
|
||||
|
||||
left = x1;
|
||||
top = y1;
|
||||
width = x2 - x1;
|
||||
height = y2 - y1;
|
||||
|
||||
}
|
||||
|
||||
void Rect::Union(int x,int y,int w,int h){
|
||||
const int mx=std::min(left,x);
|
||||
const int my=std::min(top,y);
|
||||
width = std::max(right(),x+w)-mx;
|
||||
height= std::max(bottom(),y+h)-my;
|
||||
left= mx;
|
||||
top = my;
|
||||
Union(Make(x,y,w,h));
|
||||
}
|
||||
|
||||
}//end namespace
|
||||
|
@ -644,7 +644,7 @@ void ImageView::onDraw(Canvas& canvas) {
|
||||
LOGV("DrawMatrix=%.2f,%.2f, %.2f,%.2f, %.2f,%.2f",mDrawMatrix.xx,mDrawMatrix.yx,
|
||||
mDrawMatrix.xy,mDrawMatrix.yy,mDrawMatrix.x0,mDrawMatrix.y0);
|
||||
canvas.translate(mPaddingLeft, mPaddingTop);
|
||||
canvas.set_matrix(mDrawMatrix);
|
||||
canvas.transform(mDrawMatrix);
|
||||
|
||||
mDrawable->draw(canvas);
|
||||
canvas.restore();
|
||||
|
@ -448,7 +448,7 @@ void ProgressBar::drawTrack(Canvas&canvas){
|
||||
canvas.translate(mPaddingLeft, mPaddingTop);
|
||||
}
|
||||
|
||||
const long time = SystemClock::uptimeMillis();//getDrawingTime();
|
||||
const long time = getDrawingTime();
|
||||
if (mHasAnimation) {
|
||||
mAnimation->getTransformation(time, *mTransformation);
|
||||
const float scale = mTransformation->getAlpha();
|
||||
|
@ -1902,10 +1902,12 @@ bool View::draw(Canvas&canvas,ViewGroup*parent,long drawingTime){
|
||||
// Sets the flag as early as possible to allow draw() implementations
|
||||
// to call invalidate() successfully when doing animations
|
||||
mPrivateFlags |= PFLAG_DRAWN;
|
||||
|
||||
double cx1,cy1,cx2,cy2;
|
||||
canvas.get_clip_extents(cx1,cy1,cx2,cy2);
|
||||
Rect rcc=Rect::MakeLTRB(cx1,cy1,cx2,cy2);
|
||||
if (!concatMatrix && (parentFlags & (ViewGroup::FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
|
||||
ViewGroup::FLAG_CLIP_CHILDREN)) == ViewGroup::FLAG_CLIP_CHILDREN &&
|
||||
false/*canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW)*/ &&
|
||||
rcc.intersect(mLeft, mTop, mWidth, mHeight)&& /*canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&*/
|
||||
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
|
||||
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
|
||||
return more;
|
||||
@ -2050,7 +2052,7 @@ bool View::draw(Canvas&canvas,ViewGroup*parent,long drawingTime){
|
||||
if (!scalingRequired || cache == nullptr) {
|
||||
canvas.rectangle(0,0,getWidth(), getHeight());//canvas.clipRect(0, 0, getWidth(), getHeight());
|
||||
} else {
|
||||
//canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
|
||||
//canvas.rectangle(0, 0, cache->getWidth(), cache->getHeight());
|
||||
}
|
||||
}
|
||||
canvas.clip();
|
||||
@ -4825,6 +4827,10 @@ void View::getDrawingRect(Rect& outRect) {
|
||||
outRect.height=mHeight;
|
||||
}
|
||||
|
||||
long View::getDrawingTime() const{
|
||||
return mAttachInfo ? mAttachInfo->mDrawingTime : 0;
|
||||
}
|
||||
|
||||
int View::getMeasuredWidth()const{
|
||||
return mMeasuredWidth & MEASURED_SIZE_MASK;
|
||||
}
|
||||
|
@ -527,6 +527,7 @@ public:
|
||||
void getHitRect(Rect&);
|
||||
bool pointInView(int localX,int localY,int slop);
|
||||
const Rect getDrawingRect()const;
|
||||
long getDrawingTime()const;
|
||||
virtual void getFocusedRect(Rect&r);
|
||||
virtual View& setPos(int x,int y);
|
||||
virtual View& setSize(int x,int y);
|
||||
|
@ -1235,6 +1235,7 @@ void ViewGroup::drawInvalidateRegion(Canvas&canvas){
|
||||
for(int i=0;i<num;i++){
|
||||
RectangleInt r=mInvalidRgn->get_rectangle(i);
|
||||
canvas.rectangle(r.x,r.y,r.width,r.height);
|
||||
LOGV("%p:%d(%d,%d,%d,%d)%d",this,mID,r.x,r.y,r.width,r.height,DEBUG_DRAW);
|
||||
}
|
||||
canvas.stroke();
|
||||
}
|
||||
@ -1307,7 +1308,7 @@ void ViewGroup::dispatchDraw(Canvas&canvas){
|
||||
int flags = mGroupFlags;
|
||||
|
||||
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
|
||||
bool buildCache = false;//!isHardwareAccelerated();
|
||||
bool buildCache = !isHardwareAccelerated();
|
||||
for (int i=0;i<mChildren.size();i++){
|
||||
View* child=mChildren[i];
|
||||
if ((child->mViewFlags & VISIBILITY_MASK) == VISIBLE) {
|
||||
@ -1341,10 +1342,10 @@ void ViewGroup::dispatchDraw(Canvas&canvas){
|
||||
|
||||
// We will draw our child's animation, let's reset the flag
|
||||
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
|
||||
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
|
||||
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
|
||||
|
||||
bool more = false;
|
||||
const long drawingTime = SystemClock::uptimeMillis();
|
||||
const long drawingTime = getDrawingTime();
|
||||
|
||||
//if (usingRenderNodeProperties) canvas.insertReorderBarrier();
|
||||
const int transientCount = mTransientIndices.size();
|
||||
@ -1400,8 +1401,6 @@ void ViewGroup::dispatchDraw(Canvas&canvas){
|
||||
|
||||
if (DEBUG_DRAW) onDebugDraw(canvas);
|
||||
|
||||
if (DEBUG_DRAW) drawInvalidateRegion(canvas);
|
||||
|
||||
if (clipToPadding) {
|
||||
while(clipSaveCount--)canvas.restore();
|
||||
}
|
||||
@ -1457,15 +1456,15 @@ void ViewGroup::invalidateChild(View*child,Rect&dirty){
|
||||
}else{
|
||||
transformMatrix=childMatrix;
|
||||
}
|
||||
Rect boundingRect =dirty;
|
||||
dirty= Canvas::mapRect(transformMatrix,dirty);
|
||||
LOGV("(%d,%d,%d,%d)-->(%d,%d,%d,%d) rotation=%f",boundingRect.left,boundingRect.top,boundingRect.width,boundingRect.height,
|
||||
|
||||
transformMatrix.transform_rectangle((const RectangleInt&)dirty,(RectangleInt&)dirty);
|
||||
LOGV("(1.%d,%d,%d,%d)-->(%d,%d,%d,%d) rotation=%f",boundingRect.left,boundingRect.top,boundingRect.width,boundingRect.height,
|
||||
dirty.left,dirty.top,dirty.width,dirty.height,child->getRotation());
|
||||
|
||||
}
|
||||
|
||||
View* view = parent;
|
||||
do {
|
||||
view=parent;
|
||||
View*view=parent;
|
||||
if (drawAnimation) {
|
||||
if (view) {
|
||||
view->mPrivateFlags |= PFLAG_DRAW_ANIMATION;
|
||||
@ -1488,21 +1487,20 @@ void ViewGroup::invalidateChild(View*child,Rect&dirty){
|
||||
parent = parent->invalidateChildInParent(location, dirty);
|
||||
if ( view && !view->hasIdentityMatrix() ) { // Account for transform on current parent
|
||||
Matrix m = view->getMatrix();
|
||||
dirty= Canvas::mapRect(m,dirty);
|
||||
m.transform_rectangle((const RectangleInt&)dirty,(RectangleInt&)dirty);
|
||||
}
|
||||
} while (parent);
|
||||
|
||||
//set invalidate region to rootview
|
||||
dynamic_cast<ViewGroup*>(view)->mInvalidRgn->do_union((const RectangleInt&)dirty);
|
||||
getRootView()->mInvalidRgn->do_union((const RectangleInt&)dirty);
|
||||
}
|
||||
|
||||
ViewGroup*ViewGroup::invalidateChildInParent(int* location, Rect& dirty){
|
||||
if (1||(mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {//0x20 0x8000
|
||||
// either DRAWN, or DRAWING_CACHE_VALID
|
||||
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
|
||||
!= FLAG_OPTIMIZE_INVALIDATE) {
|
||||
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
|
||||
location[CHILD_TOP_INDEX] - mScrollY);
|
||||
//mGroupFlags &=~ FLAG_CLIP_CHILDREN;
|
||||
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) {
|
||||
dirty.offset(location[CHILD_LEFT_INDEX]-mScrollX,location[CHILD_TOP_INDEX]-mScrollY);
|
||||
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
|
||||
dirty.Union(0, 0, mWidth,mHeight);
|
||||
}
|
||||
|
@ -67,9 +67,11 @@ void Window::setRegion(const RefPtr<Region>&rgn){
|
||||
}
|
||||
|
||||
void Window::draw(){
|
||||
getCanvas();
|
||||
ViewGroup::draw(*mAttachInfo->mCanvas);
|
||||
mAttachInfo->mCanvas->invalidate(mInvalidRgn);
|
||||
Canvas*canvas=getCanvas();
|
||||
mAttachInfo->mDrawingTime=SystemClock::uptimeMillis();
|
||||
ViewGroup::draw(*canvas);
|
||||
if(DEBUG_DRAW)drawInvalidateRegion(*canvas);
|
||||
canvas->invalidate(mInvalidRgn);
|
||||
mInvalidRgn->subtract(mInvalidRgn);
|
||||
}
|
||||
|
||||
@ -555,4 +557,4 @@ View*Window::inflate(Context*ctx,std::istream&stream){
|
||||
return pd.rootView;
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} //endof namespace
|
||||
|
@ -16,9 +16,14 @@ class RECTTEST:public testing::Test{
|
||||
|
||||
TEST_F(RECTTEST,intersect){
|
||||
RECT rc1={0,0,100,100};
|
||||
RECT rc2={-40,-40,100,100};
|
||||
RECT rc2={-40,-50,100,100};
|
||||
rc2.intersect(rc1);
|
||||
printf("%d,%d %d,%d\r\n",rc2.left,rc2.top,rc2.width,rc2.height);
|
||||
ASSERT_EQ(rc2.left,0);
|
||||
ASSERT_EQ(rc2.top,0);
|
||||
ASSERT_EQ(rc2.width,60);
|
||||
ASSERT_EQ(rc2.height,50);
|
||||
|
||||
}
|
||||
TEST_F(RECTTEST,intersect2){
|
||||
RECT rc1={0,0,100,100};
|
||||
@ -26,3 +31,10 @@ TEST_F(RECTTEST,intersect2){
|
||||
rc1.intersect(rc2);
|
||||
printf("%d,%d %d,%d\r\n",rc1.left,rc1.top,rc1.width,rc1.height);
|
||||
}
|
||||
|
||||
TEST_F(RECTTEST,Union){
|
||||
RECT rc1={-83,-83,566,566};
|
||||
RECT rc2={0,0,800,600};
|
||||
rc1.Union(rc2);
|
||||
printf("%d,%d %d,%d\r\n",rc1.left,rc1.top,rc1.width,rc1.height);
|
||||
}
|
||||
|
@ -159,6 +159,17 @@ TEST_F(CONTEXT,Clip1){
|
||||
ctx->get_target()->write_to_png("clip1.png");
|
||||
}
|
||||
|
||||
TEST_F(CONTEXT,Clip2){
|
||||
|
||||
ctx->reset_clip();
|
||||
ctx->arc(100,100,50,0,M_PI*2.f);
|
||||
ctx->clip();
|
||||
double x1=0,y1=0,x2=0,y2=0;
|
||||
ctx->get_clip_extents(x1,y1,x2,y2);
|
||||
std::vector<Rectangle>lst;
|
||||
ctx->copy_clip_rectangle_list(lst);
|
||||
printf("CLIPS(%f,%f,%f,%f) lst.size=%d\r\n",x1,y1,x2,y2,lst.size());
|
||||
}
|
||||
|
||||
TEST_F(CONTEXT,Mask){
|
||||
RefPtr<ImageSurface>img=ImageSurface::create_from_png("im_game.png");
|
||||
|
Loading…
Reference in New Issue
Block a user