diff options
Diffstat (limited to 'core/fxge/skia/fx_skia_device.cpp')
-rw-r--r-- | core/fxge/skia/fx_skia_device.cpp | 473 |
1 files changed, 450 insertions, 23 deletions
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp index 4b69d2fee6..c7f231a8ca 100644 --- a/core/fxge/skia/fx_skia_device.cpp +++ b/core/fxge/skia/fx_skia_device.cpp @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "core/fxge/include/fx_ge.h" - #if defined(_SKIA_SUPPORT_) #include <algorithm> #include <vector> @@ -29,6 +27,10 @@ #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/pathops/SkPathOps.h" +#ifdef SK_DEBUG +#include "third_party/skia/include/core/SkClipStack.h" +#endif + namespace { #define SHOW_SKIA_PATH 0 // set to 1 to print the path contents @@ -476,6 +478,401 @@ void ClipAngledGradient(const SkPoint pts[2], } // namespace +// Encapsulate the state used for successive text and path draws so that +// they can be combined. +class SkiaState { + public: + enum class Clip { + kSave, + kPath, + }; + + // mark all cached state as uninitialized + SkiaState() + : m_pFont(nullptr), + m_pCache(nullptr), + m_fontSize(0), + m_fillColor(0), + m_strokeColor(0), + m_blendType(0), + m_commandIndex(0), + m_drawText(false), + m_drawPath(false), + m_fillPath(false), + m_debugDisable(false) {} + + bool DrawPath(const CFX_PathData* pPathData, + const CFX_Matrix* pMatrix, + const CFX_GraphStateData* pDrawState, + uint32_t fill_color, + uint32_t stroke_color, + int fill_mode, + int blend_type, + CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + if (m_commandIndex < m_commands.count()) + FlushCommands(pDriver); + if (m_drawText) + FlushText(pDriver); + if (m_drawPath && DrawChanged(pMatrix, pDrawState, fill_color, stroke_color, + fill_mode, blend_type)) { + FlushPath(pDriver); + } + if (!m_drawPath) { + m_skPath.reset(); + m_fillPath = (fill_mode & 3) && fill_color; + m_skPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE + ? SkPath::kEvenOdd_FillType + : SkPath::kWinding_FillType); + m_drawState = *pDrawState; + m_fillColor = fill_color; + m_strokeColor = stroke_color; + m_blendType = blend_type; + m_drawMatrix = *pMatrix; + } + SkPath skPath = BuildPath(pPathData); + SkPoint delta; + if (MatrixOffset(pMatrix, &delta)) + skPath.offset(delta.fX, delta.fY); + m_skPath.addPath(skPath); + m_drawPath = true; + return true; + } + + void FlushPath(CFX_SkiaDeviceDriver* pDriver) { + SkMatrix skMatrix = ToSkMatrix(m_drawMatrix); + SkPaint skPaint; + skPaint.setAntiAlias(true); + int stroke_alpha = FXARGB_A(m_strokeColor); + if (stroke_alpha) + pDriver->PaintStroke(&skPaint, &m_drawState, skMatrix); + SkCanvas* skCanvas = pDriver->SkiaCanvas(); + skCanvas->save(); + skCanvas->concat(skMatrix); + if (m_fillPath) { + SkPath strokePath; + const SkPath* fillPath = &m_skPath; + if (stroke_alpha) { + SkAlpha fillA = SkColorGetA(m_fillColor); + SkAlpha strokeA = SkColorGetA(m_strokeColor); + if (fillA && fillA < 0xFF && strokeA && strokeA < 0xFF) { + skPaint.getFillPath(m_skPath, &strokePath); + if (Op(m_skPath, strokePath, SkPathOp::kDifference_SkPathOp, + &strokePath)) { + fillPath = &strokePath; + } + } + } + skPaint.setStyle(SkPaint::kFill_Style); + skPaint.setColor(m_fillColor); + skCanvas->drawPath(*fillPath, skPaint); + } + if (stroke_alpha) { + DebugShowSkiaPath(m_skPath); + DebugShowCanvasMatrix(skCanvas); + skPaint.setStyle(SkPaint::kStroke_Style); + skPaint.setColor(m_strokeColor); + skCanvas->drawPath(m_skPath, skPaint); + } + skCanvas->restore(); + m_drawPath = false; + } + + bool DrawText(int nChars, + const FXTEXT_CHARPOS* pCharPos, + CFX_Font* pFont, + CFX_FontCache* pCache, + const CFX_Matrix* pMatrix, + FX_FLOAT font_size, + uint32_t color, + CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + if (m_commandIndex < m_commands.count()) + FlushCommands(pDriver); + if (m_drawPath) + FlushPath(pDriver); + if (m_drawText && FontChanged(pFont, pCache, pMatrix, font_size, color)) + FlushText(pDriver); + if (!m_drawText) { + m_positions.setCount(0); + m_glyphs.setCount(0); + m_pFont = pFont; + m_pCache = pCache; + m_fontSize = font_size; + m_fillColor = color; + m_drawMatrix = *pMatrix; + } + int count = m_positions.count(); + m_positions.setCount(nChars + count); + m_glyphs.setCount(nChars + count); + for (int index = 0; index < nChars; ++index) { + const FXTEXT_CHARPOS& cp = pCharPos[index]; + m_positions[index + count] = {cp.m_OriginX, cp.m_OriginY}; + m_glyphs[index + count] = (uint16_t)cp.m_GlyphIndex; + } + SkPoint delta; + if (MatrixOffset(pMatrix, &delta)) { + for (int index = 0; index < nChars; ++index) + m_positions[index + count].offset(delta.fX, -delta.fY); + } + m_drawText = true; + return true; + } + + void FlushText(CFX_SkiaDeviceDriver* pDriver) { + SkMatrix skMatrix = ToFlippedSkMatrix(m_drawMatrix); + SkPaint skPaint; + skPaint.setAntiAlias(true); + skPaint.setColor(m_fillColor); + if (m_pFont->GetFace()) { // exclude placeholder test fonts + sk_sp<SkTypeface> typeface(SkSafeRef(m_pCache->GetDeviceCache(m_pFont))); + skPaint.setTypeface(typeface); + } + skPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + skPaint.setTextSize(m_fontSize); + skPaint.setSubpixelText(true); + SkCanvas* skCanvas = pDriver->SkiaCanvas(); + skCanvas->save(); + skCanvas->concat(skMatrix); + skCanvas->drawPosText(m_glyphs.begin(), m_glyphs.count() * 2, + m_positions.begin(), skPaint); + skCanvas->restore(); + m_drawText = false; + } + + bool SetClipFill(const CFX_PathData* pPathData, + const CFX_Matrix* pMatrix, + int fill_mode, + CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + SkPath skClipPath = BuildPath(pPathData); + skClipPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE + ? SkPath::kEvenOdd_FillType + : SkPath::kWinding_FillType); + SkMatrix skMatrix = ToSkMatrix(*pMatrix); + skClipPath.transform(skMatrix); + return SetClip(skClipPath, pDriver); + } + + bool SetClip(const SkPath& skClipPath, CFX_SkiaDeviceDriver* pDriver) { + if (m_commandIndex < m_commands.count()) { + if (m_commands[m_commandIndex] == Clip::kPath && + m_clips[m_commandIndex] == skClipPath) { + ++m_commandIndex; + return true; + } + FlushCommands(pDriver); + } + Flush(pDriver); + m_commands.push(Clip::kPath); + ++m_commandIndex; + m_clips.push_back(skClipPath); + return false; + } + + bool SetClipStroke(const CFX_PathData* pPathData, + const CFX_Matrix* pMatrix, + const CFX_GraphStateData* pGraphState, + CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + SkPath skPath = BuildPath(pPathData); + SkMatrix skMatrix = ToSkMatrix(*pMatrix); + SkPaint skPaint; + pDriver->PaintStroke(&skPaint, pGraphState, skMatrix); + SkPath dst_path; + skPaint.getFillPath(skPath, &dst_path); + dst_path.transform(skMatrix); + return SetClip(dst_path, pDriver); + } + + bool MatrixOffset(const CFX_Matrix* pMatrix, SkPoint* delta) { + delta->set(pMatrix->e - m_drawMatrix.e, pMatrix->f - m_drawMatrix.f); + if (!delta->fX && !delta->fY) + return true; + SkMatrix drawMatrix = ToSkMatrix(m_drawMatrix); + if (!(drawMatrix.getType() & ~SkMatrix::kTranslate_Mask)) + return true; + SkMatrix invDrawMatrix; + if (!drawMatrix.invert(&invDrawMatrix)) + return false; + SkMatrix invNewMatrix; + SkMatrix newMatrix = ToSkMatrix(*pMatrix); + if (!newMatrix.invert(&invNewMatrix)) + return false; + delta->set(invDrawMatrix.getTranslateX() - invNewMatrix.getTranslateX(), + invDrawMatrix.getTranslateY() - invNewMatrix.getTranslateY()); + return true; + } + + void FlushCommands(CFX_SkiaDeviceDriver* pDriver) { + if (m_commandIndex == m_commands.count()) + return; + if (m_commandIndex < m_commands.count()) + pDriver->SkiaCanvas()->restore(); + int index = m_commands.count() - 1; + if (m_commandIndex == index && m_commands[index] == Clip::kSave) + return; + for (; index > m_commandIndex; --index) { + if (m_commands[index] == Clip::kSave) + pDriver->SkiaCanvas()->restore(); + } + + if (m_commandIndex > 0) + pDriver->SkiaCanvas()->save(); + while (index > 0 && m_commands[index] != Clip::kSave) + --index; + while (++index < m_commandIndex) { + SkASSERT(m_commands[index] == Clip::kPath); + pDriver->SkiaCanvas()->clipPath(m_clips[index], SkRegion::kIntersect_Op, + true); + } + m_commands.setCount(m_commandIndex); + m_clips.resize_back(m_commandIndex); + } + + // returns true if caller should apply command to skia canvas + bool ClipSave(CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + int count = m_commands.count(); + if (m_commandIndex < count) { + if (m_commands[m_commandIndex] == Clip::kSave) { + ++m_commandIndex; + return true; + } + FlushCommands(pDriver); + } + Flush(pDriver); + m_commands.push(Clip::kSave); + ++m_commandIndex; + m_clips.push_back(m_skEmptyPath); + return false; + } + + bool ClipRestore(CFX_SkiaDeviceDriver* pDriver) { + if (m_debugDisable) + return false; + while (m_commandIndex > 0) { + if (m_commands[--m_commandIndex] == Clip::kSave) + return true; + } + Flush(pDriver); + return false; + } + + bool DrawChanged(const CFX_Matrix* pMatrix, + const CFX_GraphStateData* pState, + uint32_t fill_color, + uint32_t stroke_color, + int fill_mode, + int blend_type) { + return MatrixChanged(pMatrix, m_drawMatrix) || + StateChanged(pState, m_drawState) || fill_color != m_fillColor || + stroke_color != m_strokeColor || + ((fill_mode & 3) == FXFILL_ALTERNATE) != + (m_skPath.getFillType() == SkPath::kEvenOdd_FillType) || + blend_type != m_blendType; + } + + bool FontChanged(CFX_Font* pFont, + CFX_FontCache* pCache, + const CFX_Matrix* pMatrix, + FX_FLOAT font_size, + uint32_t color) { + return pFont != m_pFont || pCache != m_pCache || + MatrixChanged(pMatrix, m_drawMatrix) || font_size != m_fontSize || + color != m_fillColor; + } + + bool MatrixChanged(const CFX_Matrix* pMatrix, const CFX_Matrix& refMatrix) { + return pMatrix->a != refMatrix.a || pMatrix->b != refMatrix.b || + pMatrix->c != refMatrix.c || pMatrix->d != refMatrix.d; + } + + bool StateChanged(const CFX_GraphStateData* pState, + const CFX_GraphStateData& refState) { + return pState->m_LineWidth != refState.m_LineWidth || + pState->m_LineCap != refState.m_LineCap || + pState->m_LineJoin != refState.m_LineJoin || + pState->m_MiterLimit != refState.m_MiterLimit || + DashChanged(pState, refState); + } + + bool DashChanged(const CFX_GraphStateData* pState, + const CFX_GraphStateData& refState) { + if (!pState->m_DashArray && !refState.m_DashArray) + return false; + if (!pState->m_DashArray || !refState.m_DashArray) + return true; + if (pState->m_DashPhase != refState.m_DashPhase || + pState->m_DashCount != refState.m_DashCount) { + return true; + } + for (int index = 0; index < pState->m_DashCount; ++index) { + if (pState->m_DashArray[index] != refState.m_DashArray[index]) + return false; + } + return true; + } + + void Flush(CFX_SkiaDeviceDriver* pDriver) { + if (m_drawPath) + FlushPath(pDriver); + if (m_drawText) + FlushText(pDriver); + } + +#ifdef SK_DEBUG + void Dump(const CFX_SkiaDeviceDriver* pDriver) const { + SkDebugf("\n\nSkia Save Count %d:\n", pDriver->m_pCanvas->getSaveCount()); + pDriver->m_pCanvas->getClipStack()->dump(); + SkDebugf("Cache:\n"); + for (int index = 0; index < m_commands.count(); ++index) { + SkDebugf("%s ", m_commandIndex == index ? "-->" : " "); + switch (m_commands[index]) { + case Clip::kSave: + SkDebugf("Save\n"); + break; + case Clip::kPath: + m_clips[index].dump(); + break; + default: + SkDebugf("unknown\n"); + } + } + if (m_commandIndex == m_commands.count()) + SkDebugf("-->\n"); + } +#endif + + private: + SkTArray<SkPath> m_clips; // stack of clips that may be reused + SkTDArray<Clip> m_commands; // stack of clip-related commands + SkTDArray<SkPoint> m_positions; // accumulator for text positions + SkTDArray<uint16_t> m_glyphs; // accumulator for text glyphs + SkPath m_skPath; // accumulator for path contours + SkPath m_skEmptyPath; // used as placehold in the clips array + CFX_Matrix m_drawMatrix; + CFX_GraphStateData m_clipState; + CFX_GraphStateData m_drawState; + CFX_Matrix m_clipMatrix; + CFX_Font* m_pFont; + CFX_FontCache* m_pCache; + FX_FLOAT m_fontSize; + uint32_t m_fillColor; + uint32_t m_strokeColor; + int m_blendType; + int m_commandIndex; // active position in clip command stack + bool m_drawText; + bool m_drawPath; + bool m_fillPath; + bool m_debugDisable; // turn off cache for debugging +}; + // convert a stroking path to scanlines void CFX_SkiaDeviceDriver::PaintStroke(SkPaint* spaint, const CFX_GraphStateData* pGraphState, @@ -548,6 +945,7 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(CFX_DIBitmap* pBitmap, : m_pBitmap(pBitmap), m_pOriDevice(pOriDevice), m_pRecorder(nullptr), + m_pCache(new SkiaState), m_bRgbByteOrder(bRgbByteOrder), m_bGroupKnockout(bGroupKnockout) { SkBitmap skBitmap; @@ -568,6 +966,7 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y) : m_pBitmap(nullptr), m_pOriDevice(nullptr), m_pRecorder(new SkPictureRecorder), + m_pCache(new SkiaState), m_bRgbByteOrder(FALSE), m_bGroupKnockout(FALSE) { m_pRecorder->beginRecording(SkIntToScalar(size_x), SkIntToScalar(size_y)); @@ -578,16 +977,23 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder) : m_pBitmap(nullptr), m_pOriDevice(nullptr), m_pRecorder(recorder), + m_pCache(new SkiaState), m_bRgbByteOrder(FALSE), m_bGroupKnockout(FALSE) { m_pCanvas = m_pRecorder->getRecordingCanvas(); } CFX_SkiaDeviceDriver::~CFX_SkiaDeviceDriver() { + Flush(); if (!m_pRecorder) delete m_pCanvas; } +void CFX_SkiaDeviceDriver::Flush() { + m_pCache->Flush(this); + m_pCache->FlushCommands(this); +} + FX_BOOL CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, const FXTEXT_CHARPOS* pCharPos, CFX_Font* pFont, @@ -595,6 +1001,10 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, const CFX_Matrix* pObject2Device, FX_FLOAT font_size, uint32_t color) { + if (m_pCache->DrawText(nChars, pCharPos, pFont, pCache, pObject2Device, + font_size, color, this)) { + return TRUE; + } sk_sp<SkTypeface> typeface(SkSafeRef(pCache->GetDeviceCache(pFont))); SkPaint paint; paint.setAntiAlias(true); @@ -642,13 +1052,15 @@ int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const { } void CFX_SkiaDeviceDriver::SaveState() { - m_pCanvas->save(); + if (!m_pCache->ClipSave(this)) + m_pCanvas->save(); } void CFX_SkiaDeviceDriver::RestoreState(bool bKeepSaved) { - m_pCanvas->restore(); + if (!m_pCache->ClipRestore(this)) + m_pCanvas->restore(); if (bKeepSaved) - m_pCanvas->save(); + SaveState(); } FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathFill( @@ -656,9 +1068,13 @@ FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathFill( const CFX_Matrix* pObject2Device, // flips object's y-axis int fill_mode // fill mode, WINDING or ALTERNATE ) { + CFX_Matrix identity; + const CFX_Matrix* deviceMatrix = pObject2Device ? pObject2Device : &identity; + if (m_pCache->SetClipFill(pPathData, deviceMatrix, fill_mode, this)) + return TRUE; if (pPathData->GetPointCount() == 5 || pPathData->GetPointCount() == 4) { CFX_FloatRect rectf; - if (pPathData->IsRect(pObject2Device, &rectf)) { + if (pPathData->IsRect(deviceMatrix, &rectf)) { rectf.Intersect( CFX_FloatRect(0, 0, (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_WIDTH), (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_HEIGHT))); @@ -666,19 +1082,19 @@ FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathFill( SkRect skClipRect = SkRect::MakeLTRB(rectf.left, rectf.bottom, rectf.right, rectf.top); DebugDrawSkiaClipRect(m_pCanvas, skClipRect); - m_pCanvas->clipRect(skClipRect); + m_pCanvas->clipRect(skClipRect, SkRegion::kIntersect_Op, true); return TRUE; } } SkPath skClipPath = BuildPath(pPathData); - skClipPath.setFillType((fill_mode & 3) == FXFILL_WINDING - ? SkPath::kWinding_FillType - : SkPath::kEvenOdd_FillType); - SkMatrix skMatrix = ToSkMatrix(*pObject2Device); + skClipPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE + ? SkPath::kEvenOdd_FillType + : SkPath::kWinding_FillType); + SkMatrix skMatrix = ToSkMatrix(*deviceMatrix); skClipPath.transform(skMatrix); DebugShowSkiaPath(skClipPath); DebugDrawSkiaClipPath(m_pCanvas, skClipPath); - m_pCanvas->clipPath(skClipPath); + m_pCanvas->clipPath(skClipPath, SkRegion::kIntersect_Op, true); return TRUE; } @@ -688,18 +1104,18 @@ FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathStroke( const CFX_Matrix* pObject2Device, // optional transformation const CFX_GraphStateData* pGraphState // graphic state, for pen attributes ) { + if (m_pCache->SetClipStroke(pPathData, pObject2Device, pGraphState, this)) + return TRUE; // build path data SkPath skPath = BuildPath(pPathData); - skPath.setFillType(SkPath::kWinding_FillType); - SkMatrix skMatrix = ToSkMatrix(*pObject2Device); - SkPaint spaint; - PaintStroke(&spaint, pGraphState, skMatrix); + SkPaint skPaint; + PaintStroke(&skPaint, pGraphState, skMatrix); SkPath dst_path; - spaint.getFillPath(skPath, &dst_path); + skPaint.getFillPath(skPath, &dst_path); dst_path.transform(skMatrix); DebugDrawSkiaClipPath(m_pCanvas, dst_path); - m_pCanvas->clipPath(dst_path); + m_pCanvas->clipPath(dst_path, SkRegion::kIntersect_Op, true); return TRUE; } @@ -711,6 +1127,10 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( uint32_t stroke_color, // stroke color int fill_mode, // fill mode, WINDING or ALTERNATE. 0 for not filled int blend_type) { + if (m_pCache->DrawPath(pPathData, pObject2Device, pGraphState, fill_color, + stroke_color, fill_mode, blend_type, this)) { + return TRUE; + } SkIRect rect; rect.set(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); @@ -728,9 +1148,9 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( m_pCanvas->save(); m_pCanvas->concat(skMatrix); if ((fill_mode & 3) && fill_color) { - skPath.setFillType((fill_mode & 3) == FXFILL_WINDING - ? SkPath::kWinding_FillType - : SkPath::kEvenOdd_FillType); + skPath.setFillType((fill_mode & 3) == FXFILL_ALTERNATE + ? SkPath::kEvenOdd_FillType + : SkPath::kWinding_FillType); SkPath strokePath; const SkPath* fillPath = &skPath; if (pGraphState && stroke_alpha) { @@ -909,7 +1329,7 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern, } m_pCanvas->save(); if (!skClip.isEmpty()) - m_pCanvas->clipPath(skClip); + m_pCanvas->clipPath(skClip, SkRegion::kIntersect_Op, true); m_pCanvas->concat(skMatrix); m_pCanvas->drawPath(skPath, paint); m_pCanvas->restore(); @@ -1000,7 +1420,7 @@ FX_BOOL CFX_SkiaDeviceDriver::StretchDIBits(const CFX_DIBSource* pSource, m_pCanvas->save(); SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom, pClipRect->right, pClipRect->top); - m_pCanvas->clipRect(skClipRect); + m_pCanvas->clipRect(skClipRect, SkRegion::kIntersect_Op, true); void* dummy; FX_BOOL result = StartDIBits(pSource, 0xFF, argb, &m, 0, dummy, blend_type); m_pCanvas->restore(); @@ -1125,6 +1545,13 @@ void CFX_SkiaDeviceDriver::PreMultiply() { DebugVerifyBitmapIsPreMultiplied(buffer, width, height); } +void CFX_SkiaDeviceDriver::Dump() const { +#ifdef SK_DEBUG + if (m_pCache) + m_pCache->Dump(this); +#endif +} + CFX_FxgeDevice::CFX_FxgeDevice() { m_bOwnedBitmap = FALSE; } |