void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override { if (rect.width() == 0 && rect.height() == 0) { SkPoint point = getTotalMatrix().mapXY(rect.x(), rect.y()); Operation operation = { DrawPoint, SkRect::MakeXYWH(point.x(), point.y(), 0, 0) }; m_recordedOperations.append(operation); } else { Operation operation = { DrawRect, rect }; getTotalMatrix().mapRect(&operation.rect); m_recordedOperations.append(operation); } }
SkRect FindCanvas::addMatchPosH(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar position[], SkScalar constY) { SkRect r; // We only care about the positions starting at the index of our match const SkScalar* xPos = &position[index]; // This assumes that the position array is monotonic increasing // The left bounds will be the position of the left most character r.fLeft = xPos[0]; // The right bounds will be the position of the last character plus its // width int lastIndex = count - 1; r.fRight = paint.measureText(&glyphs[lastIndex], sizeof(uint16_t), 0) + xPos[lastIndex]; // Grab font metrics to determine the top and bottom of the bounds SkPaint::FontMetrics fontMetrics; paint.getFontMetrics(&fontMetrics); r.fTop = constY + fontMetrics.fAscent; r.fBottom = constY + fontMetrics.fDescent; const SkMatrix& matrix = getTotalMatrix(); matrix.mapRect(&r); SkCanvas* canvas = getWorkingCanvas(); int saveCount = canvas->save(); canvas->concat(matrix); canvas->drawPosTextH(glyphs, count * sizeof(uint16_t), xPos, constY, paint); canvas->restoreToCount(saveCount); return r; }
SkRect FindCanvas::addMatchPos(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar xPos[], SkScalar /* y */) { SkRect r; r.setEmpty(); const SkPoint* temp = reinterpret_cast<const SkPoint*> (xPos); const SkPoint* points = &temp[index]; int countInBytes = count * sizeof(uint16_t); SkPaint::FontMetrics fontMetrics; paint.getFontMetrics(&fontMetrics); // Need to check each character individually, since the heights may be // different. for (int j = 0; j < count; j++) { SkRect bounds; bounds.fLeft = points[j].fX; bounds.fRight = bounds.fLeft + paint.measureText(&glyphs[j], sizeof(uint16_t), 0); SkScalar baseline = points[j].fY; bounds.fTop = baseline + fontMetrics.fAscent; bounds.fBottom = baseline + fontMetrics.fDescent; /* Accumulate and then add the resulting rect to mMatches */ r.join(bounds); } SkMatrix matrix = getTotalMatrix(); matrix.mapRect(&r); SkCanvas* canvas = getWorkingCanvas(); int saveCount = canvas->save(); canvas->concat(matrix); canvas->drawPosText(glyphs, countInBytes, points, paint); canvas->restoreToCount(saveCount); return r; }
// Each version of addMatch returns a rectangle for a match. // Not all of the parameters are used by each version. SkRect FindCanvas::addMatchNormal(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar pos[], SkScalar y) { const uint16_t* lineStart = glyphs - index; /* Use the original paint, since "text" is in glyphs */ SkScalar before = paint.measureText(lineStart, index * sizeof(uint16_t), 0); SkRect rect; rect.fLeft = pos[0] + before; int countInBytes = count * sizeof(uint16_t); rect.fRight = paint.measureText(glyphs, countInBytes, 0) + rect.fLeft; SkPaint::FontMetrics fontMetrics; paint.getFontMetrics(&fontMetrics); SkScalar baseline = y; rect.fTop = baseline + fontMetrics.fAscent; rect.fBottom = baseline + fontMetrics.fDescent; const SkMatrix& matrix = getTotalMatrix(); matrix.mapRect(&rect); // Add the text to our picture. SkCanvas* canvas = getWorkingCanvas(); int saveCount = canvas->save(); canvas->concat(matrix); canvas->drawText(glyphs, countInBytes, pos[0] + before, y, paint); canvas->restoreToCount(saveCount); return rect; }
void onDrawRect(const SkRect& rect, const SkPaint& paint) override { if (!paint.getAnnotation()) return; Operation operation = { DrawRect, rect }; getTotalMatrix().mapRect(&operation.rect); m_recordedOperations.append(operation); }
void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) override { if (!paint.getAnnotation()) return; ASSERT_EQ(1u, count); // Only called from drawPoint(). SkPoint point = getTotalMatrix().mapXY(pts[0].x(), pts[0].y()); Operation operation = { DrawPoint, SkRect::MakeXYWH(point.x(), point.y(), 0, 0) }; m_recordedOperations.append(operation); }
void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, const ImageBuffer* imageBuffer) { SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) }; if (imageBuffer->internalSize().isEmpty()) { m_canvas->clipRect(bounds); return; } // Skia doesn't support clipping to an image, so we create a layer. The next // time restore is invoked the layer and |imageBuffer| are combined to // create the resulting image. m_state->m_clip = bounds; // Get the absolute coordinates of the stored clipping rectangle to make it // independent of any transform changes. getTotalMatrix().mapRect(&m_state->m_clip); SkCanvas::SaveFlags saveFlags = static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag); saveLayer(&bounds, 0, saveFlags); const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); if (m_trackOpaqueRegion) { SkRect opaqueRect = bitmap->isOpaque() ? m_state->m_clip : SkRect::MakeEmpty(); m_opaqueRegion.setImageMask(opaqueRect); } // Copy off the image as |imageBuffer| may be deleted before restore is invoked. if (bitmap->isImmutable()) m_state->m_imageBufferClip = *bitmap; else { // We need to make a deep-copy of the pixels themselves, so they don't // change on us between now and when we want to apply them in restore() bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); } }
void SkBBoxHierarchyRecord::setMatrix(const SkMatrix& matrix) { INHERITED::setMatrix(matrix); fStateTree->appendTransform(getTotalMatrix()); }
bool SkBBoxHierarchyRecord::concat(const SkMatrix& matrix) { bool result = INHERITED::concat(matrix); fStateTree->appendTransform(getTotalMatrix()); return result; }
bool SkBBoxHierarchyRecord::skew(SkScalar sx, SkScalar sy) { bool result = INHERITED::skew(sx, sy); fStateTree->appendTransform(getTotalMatrix()); return result; }
bool SkBBoxHierarchyRecord::rotate(SkScalar degrees) { bool result = INHERITED::rotate(degrees); fStateTree->appendTransform(getTotalMatrix()); return result; }
bool SkBBoxHierarchyRecord::translate(SkScalar dx, SkScalar dy) { bool result = INHERITED::translate(dx, dy); fStateTree->appendTransform(getTotalMatrix()); return result; }
void FindCanvas::findHelper(const void* text, size_t byteLength, const SkPaint& paint, const SkScalar positions[], SkScalar y, SkRect (FindCanvas::*addMatch)(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar positions[], SkScalar y)) { SkASSERT(paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding); SkASSERT(mMatches); GlyphSet* glyphSet = getGlyphs(paint); const int count = glyphSet->getCount(); int numCharacters = byteLength >> 1; const uint16_t* chars = (const uint16_t*) text; // This block will check to see if we are continuing from another line. If // so, the user needs to have added a space, which we do not draw. if (mWorkingIndex) { SkPoint newY; getTotalMatrix().mapXY(0, y, &newY); SkIRect workingBounds = mWorkingRegion.getBounds(); int newYInt = SkScalarRound(newY.fY); if (workingBounds.fTop > newYInt) { // The new text is above the working region, so we know it's not // a continuation. resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); } else if (workingBounds.fBottom < newYInt) { // Now we know that this line is lower than our partial match. SkPaint clonePaint(paint); clonePaint.setTextEncoding(SkPaint::kUTF8_TextEncoding); uint16_t space; clonePaint.textToGlyphs(" ", 1, &space); if (glyphSet->characterMatches(space, mWorkingIndex)) { mWorkingIndex++; if (mWorkingIndex == count) { // We already know that it is not clipped out because we // checked for that before saving the working region. insertMatchInfo(mWorkingRegion); resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); // We have found a match, so continue on this line from // scratch. } } else { resetWorkingCanvas(); mWorkingIndex = 0; mWorkingRegion.setEmpty(); } } // If neither one is true, then we are likely continuing on the same // line, but are in a new draw call because the paint has changed. In // this case, we can continue without adding a space. } // j is the position in the search text // Start off with mWorkingIndex in case we are continuing from a prior call int j = mWorkingIndex; // index is the position in the drawn text int index = 0; for ( ; index != numCharacters; index++) { if (glyphSet->characterMatches(chars[index], j)) { // The jth character in the search text matches the indexth position // in the drawn text, so increase j. j++; if (j != count) { continue; } // The last count characters match, so we found the entire // search string. int remaining = count - mWorkingIndex; int matchIndex = index - remaining + 1; // Set up a pointer to the matching text in 'chars'. const uint16_t* glyphs = chars + matchIndex; SkRect rect = (this->*addMatch)(matchIndex, paint, remaining, glyphs, positions, y); // We need an SkIRect for SkRegion operations. SkIRect iRect; rect.roundOut(&iRect); // If the rectangle is partially clipped, assume that the text is // not visible, so skip this match. if (getTotalClip().contains(iRect)) { // Want to outset the drawn rectangle by the same amount as // mOutset iRect.inset(-INTEGER_OUTSET, -INTEGER_OUTSET); SkRegion regionToAdd(iRect); if (!mWorkingRegion.isEmpty()) { // If this is on the same line as our working region, make // sure that they are close enough together that they are // supposed to be part of the same text string. // The width of two spaces has arbitrarily been chosen. const SkIRect& workingBounds = mWorkingRegion.getBounds(); if (workingBounds.fTop <= iRect.fBottom && workingBounds.fBottom >= iRect.fTop && SkIntToScalar(iRect.fLeft - workingBounds.fRight) > approximateSpaceWidth(paint)) { index = -1; // Will increase to 0 on next run // In this case, we need to start from the beginning of // the text being searched and our search term. j = 0; mWorkingIndex = 0; mWorkingRegion.setEmpty(); continue; } // Add the mWorkingRegion, which contains rectangles from // the previous line(s). regionToAdd.op(mWorkingRegion, SkRegion::kUnion_Op); } insertMatchInfo(regionToAdd); #if INCLUDE_SUBSTRING_MATCHES // Reset index to the location of the match and reset j to the // beginning, so that on the next iteration of the loop, index // will advance by 1 and we will compare the next character in // chars to the first character in the GlyphSet. index = matchIndex; #endif } else { // This match was clipped out, so begin looking at the next // character from our hidden match index = matchIndex; } // Whether the clip contained it or not, we need to start over // with our recording canvas resetWorkingCanvas(); } else { // Index needs to be set to index - j + 1. // This is a ridiculous case, but imagine the situation where the // user is looking for the string "jjog" in the drawText call for // "jjjog". The first two letters match. However, when the index // is 2, and we discover that 'o' and 'j' do not match, we should go // back to 1, where we do, in fact, have a match // FIXME: This does not work if (as in our example) "jj" is in one // draw call and "jog" is in the next. Doing so would require a // stack, keeping track of multiple possible working indeces and // regions. This is likely an uncommon case. index = index - j; // index will be increased by one on the next // iteration } // We reach here in one of two cases: // 1) We just completed a match, so any working rectangle/index is no // longer needed, and we will start over from the beginning // 2) The glyphs do not match, so we start over at the beginning of // the search string. j = 0; mWorkingIndex = 0; mWorkingRegion.setEmpty(); } // At this point, we have searched all of the text in the current drawText // call. // Keep track of a partial match that may start on this line. if (j > 0) { // if j is greater than 0, we have a partial match int relativeCount = j - mWorkingIndex; // Number of characters in this // part of the match. int partialIndex = index - relativeCount; // Index that starts our // partial match. const uint16_t* partialGlyphs = chars + partialIndex; SkRect partial = (this->*addMatch)(partialIndex, paint, relativeCount, partialGlyphs, positions, y); partial.inset(mOutset, mOutset); SkIRect dest; partial.roundOut(&dest); // Only save a partial if it is in the current clip. if (getTotalClip().contains(dest)) { mWorkingRegion.op(dest, SkRegion::kUnion_Op); mWorkingIndex = j; return; } } // This string doesn't go into the next drawText, so reset our working // variables mWorkingRegion.setEmpty(); mWorkingIndex = 0; }
void SkBBoxHierarchyRecord::didConcat(const SkMatrix& matrix) { fStateTree->appendTransform(getTotalMatrix()); INHERITED::didConcat(matrix); }