Merge "Object-based DisplayList recording"

This commit is contained in:
Chris Craik
2013-01-30 18:42:42 +00:00
committed by Android (Google) Code Review
11 changed files with 1434 additions and 1486 deletions

View File

@ -1868,7 +1868,7 @@ public abstract class HardwareRenderer {
mDebugDataProvider.setupGraphPaint(mProfilePaint, i); mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
switch (graphType) { switch (graphType) {
case GraphDataProvider.GRAPH_TYPE_BARS: case GraphDataProvider.GRAPH_TYPE_BARS:
mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint); mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
break; break;
case GraphDataProvider.GRAPH_TYPE_LINES: case GraphDataProvider.GRAPH_TYPE_LINES:
mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint); mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);

View File

@ -479,7 +479,7 @@ static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject cla
rects.push(r.fTop); rects.push(r.fTop);
rects.push(r.fRight); rects.push(r.fRight);
rects.push(r.fBottom); rects.push(r.fBottom);
count++; count += 4;
it.next(); it.next();
} }
renderer->drawRects(rects.array(), count, paint); renderer->drawRects(rects.array(), count, paint);

View File

@ -18,9 +18,8 @@
// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure // BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
// that mStart always points at the next command, not just the next item // that mStart always points at the next command, not just the next item
#define COMMAND_SIZE 2
#define NUM_COMMANDS 50 #define NUM_COMMANDS 50
#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1) #define BUFFER_SIZE ((NUM_COMMANDS) + 1)
/** /**
* DisplayListLogBuffer is a utility class which logs the most recent display * DisplayListLogBuffer is a utility class which logs the most recent display
@ -57,7 +56,7 @@ namespace uirenderer {
DisplayListLogBuffer::DisplayListLogBuffer() { DisplayListLogBuffer::DisplayListLogBuffer() {
mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int)); mBufferFirst = (OpLog*) malloc(BUFFER_SIZE * sizeof(OpLog));
mStart = mBufferFirst; mStart = mBufferFirst;
mBufferLast = mBufferFirst + BUFFER_SIZE - 1; mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
mEnd = mStart; mEnd = mStart;
@ -71,42 +70,30 @@ DisplayListLogBuffer::~DisplayListLogBuffer() {
* Called from DisplayListRenderer to output the current buffer into the * Called from DisplayListRenderer to output the current buffer into the
* specified FILE. This only happens in a dumpsys/bugreport operation. * specified FILE. This only happens in a dumpsys/bugreport operation.
*/ */
void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[]) void DisplayListLogBuffer::outputCommands(FILE *file)
{ {
int *tmpBufferPtr = mStart; OpLog* tmpBufferPtr = mStart;
while (true) { while (true) {
if (tmpBufferPtr == mEnd) { if (tmpBufferPtr == mEnd) {
break; break;
} }
int level = *tmpBufferPtr++; OpLog* nextOp = tmpBufferPtr++;
if (tmpBufferPtr > mBufferLast) { if (tmpBufferPtr > mBufferLast) {
tmpBufferPtr = mBufferFirst; tmpBufferPtr = mBufferFirst;
} }
int op = *tmpBufferPtr++;
if (tmpBufferPtr > mBufferLast) { fprintf(file, "%*s%s\n", tmpBufferPtr->level*2, "", tmpBufferPtr->label);
tmpBufferPtr = mBufferFirst;
}
uint32_t count = (level + 1) * 2;
char indent[count + 1];
for (uint32_t i = 0; i < count; i++) {
indent[i] = ' ';
}
indent[count] = '\0';
fprintf(file, "%s%s\n", indent, opNames[op]);
} }
} }
void DisplayListLogBuffer::writeCommand(int level, int op) {
writeInt(level);
writeInt(op);
}
/** /**
* Store the given value in the buffer and increment/wrap the mEnd * Store the given level and label in the buffer and increment/wrap the mEnd
* and mStart values as appropriate. * and mStart values as appropriate. Label should point to static memory.
*/ */
void DisplayListLogBuffer::writeInt(int value) { void DisplayListLogBuffer::writeCommand(int level, const char* label) {
*((int*)mEnd) = value; mEnd->level = level;
mEnd->label = label;
if (mEnd == mBufferLast) { if (mEnd == mBufferLast) {
mEnd = mBufferFirst; mEnd = mBufferFirst;
} else { } else {

View File

@ -31,19 +31,23 @@ class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
friend class Singleton<DisplayListLogBuffer>; friend class Singleton<DisplayListLogBuffer>;
public: public:
void writeCommand(int level, int op); void writeCommand(int level, const char* label);
void writeInt(int value); void outputCommands(FILE *file);
void outputCommands(FILE *file, const char* opNames[]);
bool isEmpty() { bool isEmpty() {
return (mStart == mEnd); return (mStart == mEnd);
} }
struct OpLog {
int level;
const char* label;
};
private: private:
int *mBufferFirst; // where the memory starts OpLog* mBufferFirst; // where the memory starts
int* mStart; // where the current command stream starts OpLog* mStart; // where the current command stream starts
int* mEnd; // where the current commands end OpLog* mEnd; // where the current commands end
int* mBufferLast; // where the buffer memory ends OpLog* mBufferLast; // where the buffer memory ends
}; };

1138
libs/hwui/DisplayListOp.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@
#include "DisplayListLogBuffer.h" #include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "utils/LinearAllocator.h"
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
@ -60,6 +61,18 @@ namespace uirenderer {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
class DisplayListRenderer; class DisplayListRenderer;
class DisplayListOp;
class DrawOp;
class StateOp;
/**
* Refcounted structure that holds data used in display list stream
*/
class DisplayListData: public LightRefBase<DisplayListData> {
public:
LinearAllocator allocator;
Vector<DisplayListOp*> displayListOps;
};
/** /**
* Replays recorded drawing commands. * Replays recorded drawing commands.
@ -69,66 +82,13 @@ public:
DisplayList(const DisplayListRenderer& recorder); DisplayList(const DisplayListRenderer& recorder);
ANDROID_API ~DisplayList(); ANDROID_API ~DisplayList();
// IMPORTANT: Update the intialization of OP_NAMES in the .cpp file
// when modifying this file
enum Op {
// Non-drawing operations
Save = 0,
Restore,
RestoreToCount,
SaveLayer,
SaveLayerAlpha,
Translate,
Rotate,
Scale,
Skew,
SetMatrix,
ConcatMatrix,
ClipRect,
ClipPath,
ClipRegion,
// Drawing operations
DrawDisplayList,
DrawLayer,
DrawBitmap,
DrawBitmapMatrix,
DrawBitmapRect,
DrawBitmapData,
DrawBitmapMesh,
DrawPatch,
DrawColor,
DrawRect,
DrawRoundRect,
DrawCircle,
DrawOval,
DrawArc,
DrawPath,
DrawLines,
DrawPoints,
DrawTextOnPath,
DrawPosText,
DrawText,
DrawRects,
ResetShader,
SetupShader,
ResetColorFilter,
SetupColorFilter,
ResetShadow,
SetupShadow,
ResetPaintFilter,
SetupPaintFilter,
DrawGLFunction,
};
// See flags defined in DisplayList.java // See flags defined in DisplayList.java
enum ReplayFlag { enum ReplayFlag {
kReplayFlag_ClipChildren = 0x1 kReplayFlag_ClipChildren = 0x1
}; };
static const char* OP_NAMES[];
void setViewProperties(OpenGLRenderer& renderer, uint32_t level); void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
void outputViewProperties(OpenGLRenderer& renderer, char* indent); void outputViewProperties(uint32_t level);
ANDROID_API size_t getSize(); ANDROID_API size_t getSize();
ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList); ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
@ -138,7 +98,7 @@ public:
status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0); status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
void output(OpenGLRenderer& renderer, uint32_t level = 0); void output(uint32_t level = 0);
ANDROID_API void reset(); ANDROID_API void reset();
@ -423,78 +383,6 @@ private:
const char* mText; const char* mText;
}; };
SkBitmap* getBitmap() {
return (SkBitmap*) getInt();
}
SkBitmap* getBitmapData() {
return (SkBitmap*) getInt();
}
SkiaShader* getShader() {
return (SkiaShader*) getInt();
}
SkiaColorFilter* getColorFilter() {
return (SkiaColorFilter*) getInt();
}
inline int32_t getIndex() {
return mReader.readInt();
}
inline int32_t getInt() {
return mReader.readInt();
}
inline uint32_t getUInt() {
return mReader.readU32();
}
SkMatrix* getMatrix() {
return (SkMatrix*) getInt();
}
SkPath* getPath() {
return (SkPath*) getInt();
}
SkRegion* getRegion() {
return (SkRegion*) getInt();
}
SkPaint* getPaint(OpenGLRenderer& renderer) {
return renderer.filterPaint((SkPaint*) getInt());
}
DisplayList* getDisplayList() {
return (DisplayList*) getInt();
}
inline float getFloat() {
return mReader.readScalar();
}
int32_t* getInts(uint32_t& count) {
count = getInt();
return (int32_t*) mReader.skip(count * sizeof(int32_t));
}
uint32_t* getUInts(int8_t& count) {
count = getInt();
return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
}
float* getFloats(int32_t& count) {
count = getInt();
return (float*) mReader.skip(count * sizeof(float));
}
void getText(TextContainer* text) {
size_t length = text->mByteLength = getInt();
text->mText = (const char*) mReader.skip(length);
}
Vector<SkBitmap*> mBitmapResources; Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources; Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources; Vector<SkiaColorFilter*> mFilterResources;
@ -507,7 +395,7 @@ private:
Vector<SkiaShader*> mShaders; Vector<SkiaShader*> mShaders;
Vector<Layer*> mLayers; Vector<Layer*> mLayers;
mutable SkFlattenableReadBuffer mReader; sp<DisplayListData> mDisplayListData;
size_t mSize; size_t mSize;
@ -634,8 +522,8 @@ public:
ANDROID_API void reset(); ANDROID_API void reset();
const SkWriter32& writeStream() const { sp<DisplayListData> getDisplayListData() const {
return mWriter; return mDisplayListData;
} }
const Vector<SkBitmap*>& getBitmapResources() const { const Vector<SkBitmap*>& getBitmapResources() const {
@ -683,102 +571,32 @@ public:
} }
private: private:
void insertRestoreToCount() { void insertRestoreToCount();
if (mRestoreSaveCount >= 0) { void insertTranslate();
mWriter.writeInt(DisplayList::RestoreToCount);
addInt(mRestoreSaveCount);
mRestoreSaveCount = -1;
}
}
void insertTranlate() { LinearAllocator& alloc() { return mDisplayListData->allocator; }
if (mHasTranslate) { void addStateOp(StateOp* op);
if (mTranslateX != 0.0f || mTranslateY != 0.0f) { bool addDrawOp(DrawOp* op); // returns true if op not rejected
mWriter.writeInt(DisplayList::Translate); void addOpInternal(DisplayListOp* op) {
addPoint(mTranslateX, mTranslateY);
mTranslateX = mTranslateY = 0.0f;
}
mHasTranslate = false;
}
}
inline void addOp(const DisplayList::Op drawOp) {
insertRestoreToCount(); insertRestoreToCount();
insertTranlate(); insertTranslate();
mWriter.writeInt(drawOp); mDisplayListData->displayListOps.add(op);
mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
} }
uint32_t* addOp(const DisplayList::Op drawOp, const bool reject) { template<class T>
insertRestoreToCount(); inline T* refBuffer(const T* srcBuffer, int32_t count) {
insertTranlate(); if (srcBuffer == NULL) return NULL;
mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList; T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
if (reject) { memcpy(dstBuffer, srcBuffer, count * sizeof(T));
mWriter.writeInt(OP_MAY_BE_SKIPPED_MASK | drawOp); return dstBuffer;
mWriter.writeInt(0xdeaddead);
mBufferSize = mWriter.size();
return mWriter.peek32(mBufferSize - sizeof(int32_t));
}
mWriter.writeInt(drawOp);
return NULL;
} }
inline void addSkip(uint32_t* location) { inline char* refText(const char* text, size_t byteLength) {
if (location) { return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
*location = (int32_t) (mWriter.size() - mBufferSize);
}
} }
inline void addInt(int32_t value) { inline SkPath* refPath(SkPath* path) {
mWriter.writeInt(value); if (!path) return NULL;
}
inline void addSize(uint32_t w, uint32_t h) {
mWriter.writeInt(w);
mWriter.writeInt(h);
}
void addInts(const int32_t* values, uint32_t count) {
mWriter.writeInt(count);
mWriter.write(values, count * sizeof(int32_t));
}
void addUInts(const uint32_t* values, int8_t count) {
mWriter.writeInt(count);
mWriter.write(values, count * sizeof(uint32_t));
}
inline void addFloat(float value) {
mWriter.writeScalar(value);
}
void addFloats(const float* values, int32_t count) {
mWriter.writeInt(count);
mWriter.write(values, count * sizeof(float));
}
inline void addPoint(float x, float y) {
mWriter.writeScalar(x);
mWriter.writeScalar(y);
}
inline void addBounds(float left, float top, float right, float bottom) {
mWriter.writeScalar(left);
mWriter.writeScalar(top);
mWriter.writeScalar(right);
mWriter.writeScalar(bottom);
}
inline void addText(const void* text, size_t byteLength) {
mWriter.writeInt(byteLength);
mWriter.writePad(text, byteLength);
}
inline void addPath(SkPath* path) {
if (!path) {
addInt((int) NULL);
return;
}
SkPath* pathCopy = mPathMap.valueFor(path); SkPath* pathCopy = mPathMap.valueFor(path);
if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
@ -792,13 +610,11 @@ private:
mCaches.resourceCache.incrementRefcount(path); mCaches.resourceCache.incrementRefcount(path);
mSourcePaths.add(path); mSourcePaths.add(path);
} }
return pathCopy;
addInt((int) pathCopy);
} }
inline SkPaint* addPaint(SkPaint* paint) { inline SkPaint* refPaint(SkPaint* paint) {
if (!paint) { if (!paint) {
addInt((int) NULL);
return paint; return paint;
} }
@ -810,14 +626,11 @@ private:
mPaints.add(paintCopy); mPaints.add(paintCopy);
} }
addInt((int) paintCopy);
return paintCopy; return paintCopy;
} }
inline SkRegion* addRegion(SkRegion* region) { inline SkRegion* refRegion(SkRegion* region) {
if (!region) { if (!region) {
addInt((int) NULL);
return region; return region;
} }
@ -830,53 +643,35 @@ private:
mRegions.add(regionCopy); mRegions.add(regionCopy);
} }
addInt((int) regionCopy);
return regionCopy; return regionCopy;
} }
inline void addDisplayList(DisplayList* displayList) { inline SkMatrix* refMatrix(SkMatrix* matrix) {
// TODO: To be safe, the display list should be ref-counted in the
// resources cache, but we rely on the caller (UI toolkit) to
// do the right thing for now
addInt((int) displayList);
}
inline void addMatrix(SkMatrix* matrix) {
// Copying the matrix is cheap and prevents against the user changing the original // Copying the matrix is cheap and prevents against the user changing the original
// matrix before the operation that uses it // matrix before the operation that uses it
SkMatrix* copy = new SkMatrix(*matrix); SkMatrix* copy = new SkMatrix(*matrix);
addInt((int) copy);
mMatrices.add(copy); mMatrices.add(copy);
return copy;
} }
inline void addLayer(Layer* layer) { inline SkBitmap* refBitmap(SkBitmap* bitmap) {
addInt((int) layer);
mLayers.add(layer);
mCaches.resourceCache.incrementRefcount(layer);
}
inline void addBitmap(SkBitmap* bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle // Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its // correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time, // contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case. // which doesn't seem worth the extra cycles for this unlikely case.
addInt((int) bitmap);
mBitmapResources.add(bitmap); mBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap); mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
} }
void addBitmapData(SkBitmap* bitmap) { inline SkBitmap* refBitmapData(SkBitmap* bitmap) {
addInt((int) bitmap);
mOwnedBitmapResources.add(bitmap); mOwnedBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap); mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
} }
inline void addShader(SkiaShader* shader) { inline SkiaShader* refShader(SkiaShader* shader) {
if (!shader) { if (!shader) return NULL;
addInt((int) NULL);
return;
}
SkiaShader* shaderCopy = mShaderMap.valueFor(shader); SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
// TODO: We also need to handle generation ID changes in compose shaders // TODO: We also need to handle generation ID changes in compose shaders
@ -887,14 +682,13 @@ private:
mShaders.add(shaderCopy); mShaders.add(shaderCopy);
mCaches.resourceCache.incrementRefcount(shaderCopy); mCaches.resourceCache.incrementRefcount(shaderCopy);
} }
return shaderCopy;
addInt((int) shaderCopy);
} }
inline void addColorFilter(SkiaColorFilter* colorFilter) { inline SkiaColorFilter* refColorFilter(SkiaColorFilter* colorFilter) {
addInt((int) colorFilter);
mFilterResources.add(colorFilter); mFilterResources.add(colorFilter);
mCaches.resourceCache.incrementRefcount(colorFilter); mCaches.resourceCache.incrementRefcount(colorFilter);
return colorFilter;
} }
Vector<SkBitmap*> mBitmapResources; Vector<SkBitmap*> mBitmapResources;
@ -919,12 +713,10 @@ private:
Vector<Layer*> mLayers; Vector<Layer*> mLayers;
uint32_t mBufferSize;
int mRestoreSaveCount; int mRestoreSaveCount;
Caches& mCaches; Caches& mCaches;
SkWriter32 mWriter; sp<DisplayListData> mDisplayListData;
float mTranslateX; float mTranslateX;
float mTranslateY; float mTranslateY;

View File

@ -1101,7 +1101,7 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
rects.push(r.fTop); rects.push(r.fTop);
rects.push(r.fRight); rects.push(r.fRight);
rects.push(r.fBottom); rects.push(r.fBottom);
count++; count += 4;
it.next(); it.next();
} }
@ -1744,7 +1744,7 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList,
void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) { void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
if (displayList) { if (displayList) {
displayList->output(*this, level); displayList->output(level);
} }
} }
@ -2094,7 +2094,6 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
*/ */
void OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { void OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
int color = paint->getColor(); int color = paint->getColor();
SkPaint::Style style = paint->getStyle();
SkXfermode::Mode mode = getXfermode(paint->getXfermode()); SkXfermode::Mode mode = getXfermode(paint->getXfermode());
bool isAA = paint->isAntiAlias(); bool isAA = paint->isAntiAlias();
@ -3205,8 +3204,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
Vertex mesh[count * 6]; Vertex mesh[count * 6];
Vertex* vertex = mesh; Vertex* vertex = mesh;
for (int i = 0; i < count; i++) { for (int index = 0; index < count; index += 4) {
int index = i * 4;
float l = rects[index + 0]; float l = rects[index + 0];
float t = rects[index + 1]; float t = rects[index + 1];
float r = rects[index + 2]; float r = rects[index + 2];

View File

@ -122,8 +122,6 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float rescaleX = 1.0f; float rescaleX = 1.0f;
float rescaleY = 1.0f; float rescaleY = 1.0f;
const float meshWidth = right - left;
if (xStretchCount > 0) { if (xStretchCount > 0) {
uint32_t stretchSize = 0; uint32_t stretchSize = 0;
for (uint32_t i = 1; i < mXCount; i += 2) { for (uint32_t i = 1; i < mXCount; i += 2) {

View File

@ -596,7 +596,6 @@ bool PathRenderer::convexPathPerimeterVertices(const SkPath& path, bool forceClo
SkPath::Iter iter(path, forceClose); SkPath::Iter iter(path, forceClose);
SkPoint pts[4]; SkPoint pts[4];
SkPath::Verb v; SkPath::Verb v;
Vertex* newVertex = 0;
while (SkPath::kDone_Verb != (v = iter.next(pts))) { while (SkPath::kDone_Verb != (v = iter.next(pts))) {
switch (v) { switch (v) {
case SkPath::kMove_Verb: case SkPath::kMove_Verb:

View File

@ -171,22 +171,19 @@ public:
} }
private: private:
static inline float min(float a, float b) { return (a < b) ? a : b; }
static inline float max(float a, float b) { return (a > b) ? a : b; }
void intersectWith(Rect& tmp) const { void intersectWith(Rect& tmp) const {
tmp.left = max(left, tmp.left); tmp.left = fmaxf(left, tmp.left);
tmp.top = max(top, tmp.top); tmp.top = fmaxf(top, tmp.top);
tmp.right = min(right, tmp.right); tmp.right = fminf(right, tmp.right);
tmp.bottom = min(bottom, tmp.bottom); tmp.bottom = fminf(bottom, tmp.bottom);
} }
Rect intersectWith(float l, float t, float r, float b) const { Rect intersectWith(float l, float t, float r, float b) const {
Rect tmp; Rect tmp;
tmp.left = max(left, l); tmp.left = fmaxf(left, l);
tmp.top = max(top, t); tmp.top = fmaxf(top, t);
tmp.right = min(right, r); tmp.right = fminf(right, r);
tmp.bottom = min(bottom, b); tmp.bottom = fminf(bottom, b);
return tmp; return tmp;
} }