Start objectifying GrAtlasTextBlob
BUG=skia: Review URL: https://codereview.chromium.org/1503193002
This commit is contained in:
parent
573ce20f42
commit
3660d53451
@ -77,7 +77,6 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
, fVertexEndIndex(0)
|
||||
, fGlyphStartIndex(0)
|
||||
, fGlyphEndIndex(0)
|
||||
, fTextRatio(1.0f)
|
||||
, fMaskFormat(kA8_GrMaskFormat)
|
||||
, fDrawAsDistanceFields(false)
|
||||
, fUseLCDText(false) {}
|
||||
@ -89,18 +88,44 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
, fVertexEndIndex(that.fVertexEndIndex)
|
||||
, fGlyphStartIndex(that.fGlyphStartIndex)
|
||||
, fGlyphEndIndex(that.fGlyphEndIndex)
|
||||
, fTextRatio(that.fTextRatio)
|
||||
, fMaskFormat(that.fMaskFormat)
|
||||
, fDrawAsDistanceFields(that.fDrawAsDistanceFields)
|
||||
, fUseLCDText(that.fUseLCDText) {
|
||||
}
|
||||
// Distance field text cannot draw coloremoji, and so has to fall back. However,
|
||||
// though the distance field text and the coloremoji may share the same run, they
|
||||
// will have different descriptors. If fOverrideDescriptor is non-nullptr, then it
|
||||
// will be used in place of the run's descriptor to regen texture coords
|
||||
// TODO we could have a descriptor cache, it would reduce the size of these blobs
|
||||
// significantly, and then the subrun could just have a refed pointer to the
|
||||
// correct descriptor.
|
||||
|
||||
void resetBulkUseToken() { fBulkUseToken.reset(); }
|
||||
GrBatchAtlas::BulkUseTokenUpdater* bulkUseToken() { return &fBulkUseToken; }
|
||||
void setStrike(GrBatchTextStrike* strike) { fStrike.reset(SkRef(strike)); }
|
||||
GrBatchTextStrike* strike() const { return fStrike.get(); }
|
||||
|
||||
void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
|
||||
uint64_t atlasGeneration() const { return fAtlasGeneration; }
|
||||
|
||||
size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; }
|
||||
void setVertexStartIndex(size_t vertStartIndex) { fVertexStartIndex = vertStartIndex;}
|
||||
size_t vertexStartIndex() const { return fVertexStartIndex; }
|
||||
void setVertexEndIndex(size_t vertEndIndex) { fVertexEndIndex = vertEndIndex; }
|
||||
size_t vertexEndIndex() const { return fVertexEndIndex; }
|
||||
void appendVertices(size_t vertexStride) {
|
||||
fVertexEndIndex += vertexStride * kVerticesPerGlyph;
|
||||
}
|
||||
|
||||
uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
|
||||
void setGlyphStartIndex(uint32_t glyphStartIndex) { fGlyphStartIndex = glyphStartIndex;}
|
||||
uint32_t glyphStartIndex() const { return fGlyphStartIndex; }
|
||||
void setGlyphEndIndex(uint32_t glyphEndIndex) { fGlyphEndIndex = glyphEndIndex; }
|
||||
uint32_t glyphEndIndex() const { return fGlyphEndIndex; }
|
||||
void glyphAppended() { fGlyphEndIndex++; }
|
||||
void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; }
|
||||
GrMaskFormat maskFormat() const { return fMaskFormat; }
|
||||
|
||||
// df properties
|
||||
void setUseLCDText(bool useLCDText) { fUseLCDText = useLCDText; }
|
||||
bool hasUseLCDText() const { return fUseLCDText; }
|
||||
void setDrawAsDistanceFields() { fDrawAsDistanceFields = true; }
|
||||
bool drawAsDistanceFields() const { return fDrawAsDistanceFields; }
|
||||
|
||||
private:
|
||||
GrBatchAtlas::BulkUseTokenUpdater fBulkUseToken;
|
||||
SkAutoTUnref<GrBatchTextStrike> fStrike;
|
||||
uint64_t fAtlasGeneration;
|
||||
@ -108,7 +133,6 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
size_t fVertexEndIndex;
|
||||
uint32_t fGlyphStartIndex;
|
||||
uint32_t fGlyphEndIndex;
|
||||
SkScalar fTextRatio; // df property
|
||||
GrMaskFormat fMaskFormat;
|
||||
bool fDrawAsDistanceFields; // df property
|
||||
bool fUseLCDText; // df property
|
||||
@ -119,11 +143,11 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
SubRunInfo& newSubRun = fSubRunInfo.push_back();
|
||||
SubRunInfo& prevSubRun = fSubRunInfo.fromBack(1);
|
||||
|
||||
newSubRun.fGlyphStartIndex = prevSubRun.fGlyphEndIndex;
|
||||
newSubRun.fGlyphEndIndex = prevSubRun.fGlyphEndIndex;
|
||||
newSubRun.setGlyphStartIndex(prevSubRun.glyphEndIndex());
|
||||
newSubRun.setGlyphEndIndex(prevSubRun.glyphEndIndex());
|
||||
|
||||
newSubRun.fVertexStartIndex = prevSubRun.fVertexEndIndex;
|
||||
newSubRun.fVertexEndIndex = prevSubRun.fVertexEndIndex;
|
||||
newSubRun.setVertexStartIndex(prevSubRun.vertexEndIndex());
|
||||
newSubRun.setVertexEndIndex(prevSubRun.vertexEndIndex());
|
||||
return newSubRun;
|
||||
}
|
||||
static const int kMinSubRuns = 1;
|
||||
@ -131,6 +155,11 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
SkRect fVertexBounds;
|
||||
SkSTArray<kMinSubRuns, SubRunInfo> fSubRunInfo;
|
||||
SkAutoDescriptor fDescriptor;
|
||||
|
||||
// Distance field text cannot draw coloremoji, and so has to fall back. However,
|
||||
// though the distance field text and the coloremoji may share the same run, they
|
||||
// will have different descriptors. If fOverrideDescriptor is non-nullptr, then it
|
||||
// will be used in place of the run's descriptor to regen texture coords
|
||||
SkAutoTDelete<SkAutoDescriptor> fOverrideDescriptor; // df properties
|
||||
GrColor fColor;
|
||||
bool fInitialized;
|
||||
@ -240,6 +269,12 @@ struct GrAtlasTextBlob : public SkRefCnt {
|
||||
bool hasBitmap() const { return SkToBool(fTextType & kHasBitmap_TextType); }
|
||||
void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
|
||||
void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
|
||||
void appendGlyph(Run::SubRunInfo* subrun, GrGlyph* glyph) {
|
||||
this->fGlyphs[subrun->glyphEndIndex()] = glyph;
|
||||
subrun->glyphAppended();
|
||||
}
|
||||
|
||||
static const int kVerticesPerGlyph = 4;
|
||||
|
||||
#ifdef CACHE_SANITY_CHECK
|
||||
static void AssertEqual(const GrAtlasTextBlob&, const GrAtlasTextBlob&);
|
||||
|
@ -466,11 +466,11 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
|
||||
PerSubRunInfo& newRun = cacheBlob->fRuns[run].fSubRunInfo.back();
|
||||
PerSubRunInfo& lastRun = cacheBlob->fRuns[run - 1].fSubRunInfo.back();
|
||||
|
||||
newRun.fVertexStartIndex = lastRun.fVertexEndIndex;
|
||||
newRun.fVertexEndIndex = lastRun.fVertexEndIndex;
|
||||
newRun.setVertexStartIndex(lastRun.vertexEndIndex());
|
||||
newRun.setVertexEndIndex(lastRun.vertexEndIndex());
|
||||
|
||||
newRun.fGlyphStartIndex = lastRun.fGlyphEndIndex;
|
||||
newRun.fGlyphEndIndex = lastRun.fGlyphEndIndex;
|
||||
newRun.setGlyphStartIndex(lastRun.glyphEndIndex());
|
||||
newRun.setGlyphEndIndex(lastRun.glyphEndIndex());
|
||||
}
|
||||
|
||||
if (this->canDrawAsDistanceFields(runPaint, viewMatrix)) {
|
||||
@ -480,8 +480,8 @@ void GrAtlasTextContext::regenerateTextBlob(GrAtlasTextBlob* cacheBlob,
|
||||
this->initDistanceFieldPaint(cacheBlob, &dfPaint, &textRatio, viewMatrix);
|
||||
Run& runIdx = cacheBlob->fRuns[run];
|
||||
PerSubRunInfo& subRun = runIdx.fSubRunInfo.back();
|
||||
subRun.fUseLCDText = runPaint.isLCDRenderText();
|
||||
subRun.fDrawAsDistanceFields = true;
|
||||
subRun.setUseLCDText(runPaint.isLCDRenderText());
|
||||
subRun.setDrawAsDistanceFields();
|
||||
|
||||
SkTDArray<char> fallbackTxt;
|
||||
SkTDArray<SkScalar> fallbackPos;
|
||||
@ -641,8 +641,8 @@ GrAtlasTextContext::setupDFBlob(int glyphCount, const SkPaint& origPaint,
|
||||
blob->fViewMatrix = viewMatrix;
|
||||
Run& run = blob->fRuns[0];
|
||||
PerSubRunInfo& subRun = run.fSubRunInfo.back();
|
||||
subRun.fUseLCDText = origPaint.isLCDRenderText();
|
||||
subRun.fDrawAsDistanceFields = true;
|
||||
subRun.setUseLCDText(origPaint.isLCDRenderText());
|
||||
subRun.setDrawAsDistanceFields();
|
||||
|
||||
return blob;
|
||||
}
|
||||
@ -1007,11 +1007,11 @@ void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrMaskFormat format = glyph->fMaskFormat;
|
||||
|
||||
PerSubRunInfo* subRun = &run.fSubRunInfo.back();
|
||||
if (run.fInitialized && subRun->fMaskFormat != format) {
|
||||
if (run.fInitialized && subRun->maskFormat() != format) {
|
||||
subRun = &run.push_back();
|
||||
subRun->fStrike.reset(SkRef(fCurrStrike));
|
||||
subRun->setStrike(fCurrStrike);
|
||||
} else if (!run.fInitialized) {
|
||||
subRun->fStrike.reset(SkRef(fCurrStrike));
|
||||
subRun->setStrike(SkRef(fCurrStrike));
|
||||
}
|
||||
|
||||
run.fInitialized = true;
|
||||
@ -1023,7 +1023,7 @@ void GrAtlasTextContext::bmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
r.fTop = SkIntToScalar(y);
|
||||
r.fRight = r.fLeft + SkIntToScalar(width);
|
||||
r.fBottom = r.fTop + SkIntToScalar(height);
|
||||
subRun->fMaskFormat = format;
|
||||
subRun->setMaskFormat(format);
|
||||
this->appendGlyphCommon(blob, &run, subRun, r, color, vertexStride, kA8_GrMaskFormat == format,
|
||||
glyph);
|
||||
}
|
||||
@ -1075,16 +1075,16 @@ bool GrAtlasTextContext::dfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
|
||||
PerSubRunInfo* subRun = &run.fSubRunInfo.back();
|
||||
if (!run.fInitialized) {
|
||||
subRun->fStrike.reset(SkRef(fCurrStrike));
|
||||
subRun->setStrike(fCurrStrike);
|
||||
}
|
||||
run.fInitialized = true;
|
||||
SkASSERT(glyph->fMaskFormat == kA8_GrMaskFormat);
|
||||
subRun->fMaskFormat = kA8_GrMaskFormat;
|
||||
subRun->setMaskFormat(kA8_GrMaskFormat);
|
||||
|
||||
size_t vertexStride = GrAtlasTextBatch::GetVertexStrideDf(kA8_GrMaskFormat,
|
||||
subRun->fUseLCDText);
|
||||
subRun->hasUseLCDText());
|
||||
|
||||
bool useColorVerts = !subRun->fUseLCDText;
|
||||
bool useColorVerts = !subRun->hasUseLCDText();
|
||||
this->appendGlyphCommon(blob, &run, subRun, glyphRect, color, vertexStride, useColorVerts,
|
||||
glyph);
|
||||
return true;
|
||||
@ -1110,11 +1110,11 @@ inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* ru
|
||||
const SkRect& positions, GrColor color,
|
||||
size_t vertexStride, bool useVertexColor,
|
||||
GrGlyph* glyph) {
|
||||
blob->fGlyphs[subRun->fGlyphEndIndex] = glyph;
|
||||
blob->appendGlyph(subRun, glyph);
|
||||
run->fVertexBounds.joinNonEmptyArg(positions);
|
||||
run->fColor = color;
|
||||
|
||||
intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->fVertexEndIndex);
|
||||
intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + subRun->vertexEndIndex());
|
||||
|
||||
if (useVertexColor) {
|
||||
// V0
|
||||
@ -1163,9 +1163,7 @@ inline void GrAtlasTextContext::appendGlyphCommon(GrAtlasTextBlob* blob, Run* ru
|
||||
position = reinterpret_cast<SkPoint*>(vertex);
|
||||
position->set(positions.fRight, positions.fTop);
|
||||
}
|
||||
|
||||
subRun->fGlyphEndIndex++;
|
||||
subRun->fVertexEndIndex += vertexStride * GrAtlasTextBatch::kVerticesPerGlyph;
|
||||
subRun->appendVertices(vertexStride);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::flushRunAsPaths(GrDrawContext* dc,
|
||||
@ -1211,7 +1209,7 @@ GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo&
|
||||
int glyphCount, int run, int subRun,
|
||||
GrColor color, SkScalar transX, SkScalar transY,
|
||||
const SkPaint& skPaint) {
|
||||
GrMaskFormat format = info.fMaskFormat;
|
||||
GrMaskFormat format = info.maskFormat();
|
||||
GrColor subRunColor;
|
||||
if (kARGB_GrMaskFormat == format) {
|
||||
uint8_t paintAlpha = skPaint.getAlpha();
|
||||
@ -1221,7 +1219,7 @@ GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo&
|
||||
}
|
||||
|
||||
GrAtlasTextBatch* batch;
|
||||
if (info.fDrawAsDistanceFields) {
|
||||
if (info.drawAsDistanceFields()) {
|
||||
SkColor filteredColor;
|
||||
SkColorFilter* colorFilter = skPaint.getColorFilter();
|
||||
if (colorFilter) {
|
||||
@ -1232,7 +1230,7 @@ GrAtlasTextContext::createBatch(GrAtlasTextBlob* cacheBlob, const PerSubRunInfo&
|
||||
bool useBGR = SkPixelGeometryIsBGR(fSurfaceProps.pixelGeometry());
|
||||
batch = GrAtlasTextBatch::CreateDistanceField(glyphCount, fContext->getBatchFontCache(),
|
||||
fDistanceAdjustTable, filteredColor,
|
||||
info.fUseLCDText, useBGR);
|
||||
info.hasUseLCDText(), useBGR);
|
||||
} else {
|
||||
batch = GrAtlasTextBatch::CreateBitmap(format, glyphCount, fContext->getBatchFontCache());
|
||||
}
|
||||
@ -1254,7 +1252,7 @@ inline void GrAtlasTextContext::flushRun(GrDrawContext* dc, GrPipelineBuilder* p
|
||||
const SkPaint& skPaint) {
|
||||
for (int subRun = 0; subRun < cacheBlob->fRuns[run].fSubRunInfo.count(); subRun++) {
|
||||
const PerSubRunInfo& info = cacheBlob->fRuns[run].fSubRunInfo[subRun];
|
||||
int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
|
||||
int glyphCount = info.glyphCount();
|
||||
if (0 == glyphCount) {
|
||||
continue;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
|
||||
static_assert(!regenGlyphs || regenTexCoords, "must regenTexCoords along regenGlyphs");
|
||||
GrBatchTextStrike* strike = nullptr;
|
||||
if (regenTexCoords) {
|
||||
info->fBulkUseToken.reset();
|
||||
info->resetBulkUseToken();
|
||||
|
||||
// We can reuse if we have a valid strike and our descriptors / typeface are the
|
||||
// same. The override descriptor is only for the non distance field text within
|
||||
@ -151,14 +151,14 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
|
||||
*desc = newDesc;
|
||||
*cache = SkGlyphCache::DetachCache(run->fTypeface, *desc);
|
||||
*scaler = GrTextContext::GetGrFontScaler(*cache);
|
||||
strike = info->fStrike;
|
||||
strike = info->strike();
|
||||
*typeface = run->fTypeface;
|
||||
}
|
||||
|
||||
if (regenGlyphs) {
|
||||
strike = fFontCache->getStrike(*scaler);
|
||||
} else {
|
||||
strike = info->fStrike;
|
||||
strike = info->strike();
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
|
||||
for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
|
||||
GrGlyph* glyph = nullptr;
|
||||
if (regenTexCoords) {
|
||||
size_t glyphOffset = glyphIdx + info->fGlyphStartIndex;
|
||||
size_t glyphOffset = glyphIdx + info->glyphStartIndex();
|
||||
|
||||
if (regenGlyphs) {
|
||||
// Get the id from the old glyph, and use the new strike to lookup
|
||||
@ -190,12 +190,12 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
|
||||
this->maskFormat());
|
||||
SkASSERT(success);
|
||||
}
|
||||
fFontCache->addGlyphToBulkAndSetUseToken(&info->fBulkUseToken, glyph,
|
||||
fFontCache->addGlyphToBulkAndSetUseToken(info->bulkUseToken(), glyph,
|
||||
target->currentToken());
|
||||
}
|
||||
|
||||
intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices);
|
||||
vertex += info->fVertexStartIndex;
|
||||
vertex += info->vertexStartIndex();
|
||||
vertex += vertexStride * glyphIdx * GrAtlasTextBatch::kVerticesPerGlyph;
|
||||
regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
|
||||
this->usesDistanceFields(), transX,
|
||||
@ -207,10 +207,10 @@ inline void GrAtlasTextBatch::regenBlob(Target* target, FlushInfo* flushInfo, Bl
|
||||
run->fColor = color;
|
||||
if (regenTexCoords) {
|
||||
if (regenGlyphs) {
|
||||
info->fStrike.reset(SkRef(strike));
|
||||
info->setStrike(strike);
|
||||
}
|
||||
info->fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
|
||||
fFontCache->atlasGeneration(this->maskFormat());
|
||||
info->setAtlasGeneration(brokenRun ? GrBatchAtlas::kInvalidAtlasGeneration :
|
||||
fFontCache->atlasGeneration(this->maskFormat()));
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -378,8 +378,8 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
|
||||
// new strike, we instead keep our ref to the old strike and use the packed ids from
|
||||
// it. These ids will still be valid as long as we hold the ref. When we are done
|
||||
// updating our cache of the GrGlyph*s, we drop our ref on the old strike
|
||||
bool regenerateGlyphs = info.fStrike->isAbandoned();
|
||||
bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
|
||||
bool regenerateGlyphs = info.strike()->isAbandoned();
|
||||
bool regenerateTextureCoords = info.atlasGeneration() != currentAtlasGen ||
|
||||
regenerateGlyphs;
|
||||
bool regenerateColors;
|
||||
if (usesDistanceFields) {
|
||||
@ -388,7 +388,7 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
|
||||
regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != args.fColor;
|
||||
}
|
||||
bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
|
||||
int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
|
||||
int glyphCount = info.glyphCount();
|
||||
|
||||
uint32_t regenMaskBits = kNoRegen;
|
||||
regenMaskBits |= regeneratePositions ? kRegenPos : 0;
|
||||
@ -416,13 +416,14 @@ void GrAtlasTextBatch::onPrepareDraws(Target* target) const {
|
||||
|
||||
// set use tokens for all of the glyphs in our subrun. This is only valid if we
|
||||
// have a valid atlas generation
|
||||
fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken(), maskFormat);
|
||||
fFontCache->setUseTokenBulk(*info.bulkUseToken(), target->currentToken(),
|
||||
maskFormat);
|
||||
break;
|
||||
}
|
||||
|
||||
// now copy all vertices
|
||||
size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
|
||||
memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
|
||||
size_t byteCount = info.byteCount();
|
||||
memcpy(currVertex, blob->fVertices + info.vertexStartIndex(), byteCount);
|
||||
|
||||
currVertex += byteCount;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public:
|
||||
// position + local coord
|
||||
static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
|
||||
static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
|
||||
static const int kVerticesPerGlyph = 4;
|
||||
static const int kVerticesPerGlyph = GrAtlasTextBlob::kVerticesPerGlyph;
|
||||
static const int kIndicesPerGlyph = 6;
|
||||
|
||||
typedef GrAtlasTextContext::DistanceAdjustTable DistanceAdjustTable;
|
||||
@ -89,7 +89,7 @@ public:
|
||||
// We don't yet position distance field text on the cpu, so we have to map the vertex bounds
|
||||
// into device space
|
||||
const Run& run = geo.fBlob->fRuns[geo.fRun];
|
||||
if (run.fSubRunInfo[geo.fSubRun].fDrawAsDistanceFields) {
|
||||
if (run.fSubRunInfo[geo.fSubRun].drawAsDistanceFields()) {
|
||||
SkRect bounds = run.fVertexBounds;
|
||||
fBatch.fViewMatrix.mapRect(&bounds);
|
||||
this->setBounds(bounds);
|
||||
|
Loading…
Reference in New Issue
Block a user