From 399be5bf559f72d4649a60320a3d802f6b21780b Mon Sep 17 00:00:00 2001 From: Cary Clark Date: Mon, 14 Mar 2016 16:51:29 -0400 Subject: Add bitmaps and skp output to Skia port This is a first-cut at supporting bitmaps. Also added a --skp option to pdfium_test to generate a Skia picture file. The picture file can be loaded in Skia's SampleApp, debugger, or skiaserver to examine the generated picture. (This also includes fixes suggested in the prior Skia CL. My apologies for fat-fingers abandoning that one.) R=dsinclair@chromium.org, tsepez@chromium.org, dsinclair Review URL: https://codereview.chromium.org/1776313002 . --- BUILD.gn | 8 +- core/fxge/agg/fx_agg_driver.h | 1 + core/fxge/skia/fx_skia_device.cpp | 342 ++++++++++++++++++++++++++++---------- core/fxge/skia/fx_skia_device.h | 8 +- core/include/fxge/fx_ge.h | 6 + fpdfsdk/fpdfformfill.cpp | 50 ++++-- fpdfsdk/fpdfview.cpp | 22 ++- public/fpdf_formfill.h | 12 ++ public/fpdfview.h | 7 + samples/BUILD.gn | 8 +- samples/pdfium_test.cc | 54 +++++- samples/samples.gyp | 6 + 12 files changed, 412 insertions(+), 112 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index d0de412f08..c9c5b016aa 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -617,12 +617,8 @@ static_library("fxge") { ] if (pdf_use_skia) { - sources = [ - "core/fxge/skia/fx_skia_device.cpp", - ] - deps = [ - "//skia", - ] + sources += [ "core/src/fxge/skia/fx_skia_device.cpp" ] + deps += [ "//skia" ] } if (is_win) { diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h index 3631016fea..ea4c36059b 100644 --- a/core/fxge/agg/fx_agg_driver.h +++ b/core/fxge/agg/fx_agg_driver.h @@ -134,6 +134,7 @@ class CFX_AggDeviceDriver : public IFX_RenderDeviceDriver { void SetClipMask(agg::rasterizer_scanline_aa& rasterizer); virtual uint8_t* GetBuffer() const; + const CFX_DIBitmap* GetBitmap() const { return m_pBitmap; } private: CFX_DIBitmap* m_pBitmap; diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp index 10eebe496c..acebf35eb8 100644 --- a/core/fxge/skia/fx_skia_device.cpp +++ b/core/fxge/skia/fx_skia_device.cpp @@ -10,10 +10,57 @@ #include "core/fxge/agg/fx_agg_driver.h" #include "core/fxge/skia/fx_skia_device.h" -#include "SkCanvas.h" -#include "SkDashPathEffect.h" -#include "SkPaint.h" -#include "SkPath.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorPriv.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkStream.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/include/effects/SkDashPathEffect.h" + +#define SHOW_SKIA_PATH 0 // set to 1 to print the path contents +#define DRAW_SKIA_CLIP 0 // set to 1 to draw a green rectangle around the clip + +static void DebugShowSkiaPath(const SkPath& path) { +#if SHOW_SKIA_PATH + char buffer[4096]; + sk_bzero(buffer, sizeof(buffer)); + SkMemoryWStream stream(buffer, sizeof(buffer)); + path.dump(&stream, false, false); + printf("%s\n", buffer); +#endif // SHOW_SKIA_PATH +} + +#if DRAW_SKIA_CLIP + +static SkPaint DebugClipPaint() { + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorGREEN); + paint.setStyle(SkPaint::kStroke_Style); + return paint; +} + +static void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) { + SkPaint paint = DebugClipPaint(); + canvas->drawRect(rect, paint); +} + +static void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) { + SkPaint paint = DebugClipPaint(); + canvas->drawPath(path, paint); +} + +#else // DRAW_SKIA_CLIP + +static void DebugDrawSkiaClipRect(SkCanvas* canvas, const SkRect& rect) {} +static void DebugDrawSkiaClipPath(SkCanvas* canvas, const SkPath& path) {} + +#endif // DRAW_SKIA_CLIP + +#undef SHOW_SKIA_PATH +#undef DRAW_SKIA_CLIP static SkPath BuildPath(const CFX_PathData* pPathData, const CFX_Matrix* pObject2Device) { @@ -97,7 +144,7 @@ void CFX_SkiaDeviceDriver::PaintStroke(SkPaint* spaint, ->unref(); } spaint->setStyle(SkPaint::kStroke_Style); - spaint->setAntiAlias(TRUE); + spaint->setAntiAlias(true); spaint->setStrokeWidth(width); spaint->setStrokeMiter(pGraphState->m_MiterLimit); spaint->setStrokeCap(cap); @@ -108,24 +155,40 @@ CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(CFX_DIBitmap* pBitmap, int dither_bits, FX_BOOL bRgbByteOrder, CFX_DIBitmap* pOriDevice, - FX_BOOL bGroupKnockout) { + FX_BOOL bGroupKnockout) + : m_pRecorder(nullptr) { m_pAggDriver = new CFX_AggDeviceDriver(pBitmap, dither_bits, bRgbByteOrder, pOriDevice, bGroupKnockout); SkBitmap skBitmap; - const CFX_DIBitmap* bitmap = m_pAggDriver->m_pBitmap; + const CFX_DIBitmap* bitmap = m_pAggDriver->GetBitmap(); SkImageInfo imageInfo = SkImageInfo::Make(bitmap->GetWidth(), bitmap->GetHeight(), kN32_SkColorType, kOpaque_SkAlphaType); skBitmap.installPixels(imageInfo, bitmap->GetBuffer(), bitmap->GetPitch(), nullptr, /* to do : set color table */ nullptr, nullptr); - m_canvas = new SkCanvas(skBitmap); + m_pCanvas = new SkCanvas(skBitmap); + m_ditherBits = dither_bits; +} + +CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(int size_x, int size_y) + : m_pRecorder(new SkPictureRecorder) { + m_pAggDriver = nullptr; + m_pRecorder->beginRecording(SkIntToScalar(size_x), SkIntToScalar(size_y)); + m_pCanvas = m_pRecorder->getRecordingCanvas(); + m_ditherBits = 0; +} + +CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkPictureRecorder* recorder) + : m_pRecorder(recorder) { + m_pAggDriver = nullptr; + m_pCanvas = m_pRecorder->getRecordingCanvas(); + m_ditherBits = 0; } CFX_SkiaDeviceDriver::~CFX_SkiaDeviceDriver() { -#if 0 // TODO(caryclark) : mismatch on allocator ? - delete m_canvas; -#endif + if (!m_pRecorder) + delete m_pCanvas; delete m_pAggDriver; } @@ -138,66 +201,97 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawDeviceText(int nChars, FX_DWORD color, int alpha_flag, void* pIccTransform) { - return m_pAggDriver->DrawDeviceText(nChars, pCharPos, pFont, pCache, - pObject2Device, font_size, color, - alpha_flag, pIccTransform); + SkAutoTUnref typeface(SkTypeface::CreateFromStream( + new SkMemoryStream(pFont->GetFontData(), pFont->GetSize()))); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(color); + paint.setTypeface(typeface); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setTextSize(font_size); + m_pCanvas->save(); + SkMatrix skMatrix; + const CFX_Matrix& m = *pObject2Device; + // note that PDF's y-axis goes up; Skia's y-axis goes down + skMatrix.setAll(m.a, m.b, m.e, -m.c, -m.d, m.f, 0, 0, 1); + m_pCanvas->concat(skMatrix); + for (int index = 0; index < nChars; ++index) { + const FXTEXT_CHARPOS& cp = pCharPos[index]; + uint16_t glyph = (uint16_t)cp.m_GlyphIndex; + m_pCanvas->drawText(&glyph, 2, cp.m_OriginX, cp.m_OriginY, paint); + } + m_pCanvas->restore(); + return TRUE; } int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) { - return m_pAggDriver->GetDeviceCaps(caps_id); + switch (caps_id) { + case FXDC_DEVICE_CLASS: + return FXDC_DISPLAY; + case FXDC_PIXEL_WIDTH: + return m_pCanvas->imageInfo().width(); + case FXDC_PIXEL_HEIGHT: + return m_pCanvas->imageInfo().height(); + case FXDC_BITS_PIXEL: + return 32; + case FXDC_HORZ_SIZE: + case FXDC_VERT_SIZE: + return 0; + case FXDC_RENDER_CAPS: + return FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | + FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT; + case FXDC_DITHER_BITS: + return m_ditherBits; + } + return 0; } void CFX_SkiaDeviceDriver::SaveState() { - m_canvas->save(); - m_pAggDriver->SaveState(); + m_pCanvas->save(); + if (m_pAggDriver) + m_pAggDriver->SaveState(); } void CFX_SkiaDeviceDriver::RestoreState(FX_BOOL bKeepSaved) { - m_pAggDriver->RestoreState(bKeepSaved); - m_canvas->restore(); + if (m_pAggDriver) + m_pAggDriver->RestoreState(bKeepSaved); + m_pCanvas->restore(); + if (bKeepSaved) + m_pCanvas->save(); } void CFX_SkiaDeviceDriver::SetClipMask( agg::rasterizer_scanline_aa& rasterizer) { - m_pAggDriver->SetClipMask(rasterizer); + if (m_pAggDriver) + m_pAggDriver->SetClipMask(rasterizer); } FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathFill( const CFX_PathData* pPathData, // path info - const CFX_Matrix* pObject2Device, // optional transformation + const CFX_Matrix* pObject2Device, // flips object's y-axis int fill_mode // fill mode, WINDING or ALTERNATE ) { - if (!m_pAggDriver->m_pClipRgn) { - m_pAggDriver->m_pClipRgn = new CFX_ClipRgn( - GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); - } - if (pPathData->GetPointCount() == 5 || pPathData->GetPointCount() == 4) { CFX_FloatRect rectf; if (pPathData->IsRect(pObject2Device, &rectf)) { rectf.Intersect( CFX_FloatRect(0, 0, (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_WIDTH), (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_HEIGHT))); - FX_RECT rect = rectf.GetOutterRect(); - m_pAggDriver->m_pClipRgn->IntersectRect(rect); + // note that PDF's y-axis goes up; Skia's y-axis goes down + SkRect skClipRect = + SkRect::MakeLTRB(rectf.left, rectf.bottom, rectf.right, rectf.top); + DebugDrawSkiaClipRect(m_pCanvas, skClipRect); + m_pCanvas->clipRect(skClipRect); return TRUE; } } - SkPath clip = BuildPath(pPathData, pObject2Device); - clip.setFillType((fill_mode & 3) == FXFILL_WINDING - ? SkPath::kWinding_FillType - : SkPath::kEvenOdd_FillType); - const CFX_Matrix& m = *pObject2Device; -#if 0 - // TODO(caryclark) : don't clip quite yet - // need to understand how to save/restore to balance the clip - printf("m:(%g,%g,%g) (%g,%g,%g)\n", m.a, m.b, m.c, m.d, m.e, m.f); - clip.dump(); - SkMatrix skMatrix; - skMatrix.setAll(m.a, m.b, m.c, m.d, m.e, m.f, 0, 0, 1); - m_canvas->setMatrix(skMatrix); - m_canvas->clipPath(clip, SkRegion::kReplace_Op); -#endif + SkPath skClipPath = BuildPath(pPathData, pObject2Device); + skClipPath.setFillType((fill_mode & 3) == FXFILL_WINDING + ? SkPath::kWinding_FillType + : SkPath::kEvenOdd_FillType); + DebugShowSkiaPath(skClipPath); + DebugDrawSkiaClipPath(m_pCanvas, skClipPath); + m_pCanvas->clipPath(skClipPath); return TRUE; } @@ -207,11 +301,6 @@ FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathStroke( const CFX_Matrix* pObject2Device, // optional transformation const CFX_GraphStateData* pGraphState // graphic state, for pen attributes ) { - if (!m_pAggDriver->m_pClipRgn) { - m_pAggDriver->m_pClipRgn = new CFX_ClipRgn( - GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); - } - // build path data SkPath skPath = BuildPath(pPathData, NULL); skPath.setFillType(SkPath::kWinding_FillType); @@ -220,15 +309,8 @@ FX_BOOL CFX_SkiaDeviceDriver::SetClip_PathStroke( PaintStroke(&spaint, pGraphState); SkPath dst_path; spaint.getFillPath(skPath, &dst_path); -#if 01 - SkMatrix skMatrix; - const CFX_Matrix& m = *pObject2Device; - skMatrix.setAll(m.a, m.b, m.c, m.d, m.e, m.f, 0, 0, 1); - m_canvas->setMatrix(skMatrix); - // TODO(caryclark) : don't clip quite yet - // need to understand how to save/restore so that clip is later undone - m_canvas->clipPath(dst_path, SkRegion::kReplace_Op); -#endif + DebugDrawSkiaClipPath(m_pCanvas, dst_path); + m_pCanvas->clipPath(dst_path); return TRUE; } @@ -239,8 +321,10 @@ FX_BOOL CFX_SkiaDeviceDriver::RenderRasterizer( FX_BOOL bGroupKnockout, int alpha_flag, void* pIccTransform) { - return m_pAggDriver->RenderRasterizer( - rasterizer, color, bFullCover, bGroupKnockout, alpha_flag, pIccTransform); + return m_pAggDriver && + m_pAggDriver->RenderRasterizer(rasterizer, color, bFullCover, + bGroupKnockout, alpha_flag, + pIccTransform); } FX_BOOL CFX_SkiaDeviceDriver::DrawPath( @@ -253,14 +337,12 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( int alpha_flag, void* pIccTransform, int blend_type) { - if (!GetBuffer()) - return TRUE; SkIRect rect; rect.set(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); SkPath skPath = BuildPath(pPathData, pObject2Device); SkPaint spaint; - spaint.setAntiAlias(TRUE); + spaint.setAntiAlias(true); if ((fill_mode & 3) && fill_color) { skPath.setFillType((fill_mode & 3) == FXFILL_WINDING ? SkPath::kWinding_FillType @@ -268,7 +350,7 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( spaint.setStyle(SkPaint::kFill_Style); spaint.setColor(fill_color); - m_canvas->drawPath(skPath, spaint); + m_pCanvas->drawPath(skPath, spaint); } int stroke_alpha = FXGETFLAG_COLORTYPE(alpha_flag) ? FXGETFLAG_ALPHA_STROKE(alpha_flag) @@ -277,7 +359,7 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( if (pGraphState && stroke_alpha) { spaint.setColor(stroke_color); PaintStroke(&spaint, pGraphState); - m_canvas->drawPath(skPath, spaint); + m_pCanvas->drawPath(skPath, spaint); } return TRUE; @@ -288,7 +370,8 @@ FX_BOOL CFX_SkiaDeviceDriver::SetPixel(int x, FX_DWORD color, int alpha_flag, void* pIccTransform) { - return m_pAggDriver->SetPixel(x, y, color, alpha_flag, pIccTransform); + return m_pAggDriver && + m_pAggDriver->SetPixel(x, y, color, alpha_flag, pIccTransform); } FX_BOOL CFX_SkiaDeviceDriver::FillRect(const FX_RECT* pRect, @@ -300,14 +383,20 @@ FX_BOOL CFX_SkiaDeviceDriver::FillRect(const FX_RECT* pRect, spaint.setAntiAlias(true); spaint.setColor(fill_color); - m_canvas->drawRect( + m_pCanvas->drawRect( SkRect::MakeLTRB(pRect->left, pRect->top, pRect->right, pRect->bottom), spaint); return TRUE; } FX_BOOL CFX_SkiaDeviceDriver::GetClipBox(FX_RECT* pRect) { - return m_pAggDriver->GetClipBox(pRect); + // TODO(caryclark) call m_canvas->getClipDeviceBounds() instead + pRect->left = 0; + pRect->top = 0; + const SkImageInfo& canvasSize = m_pCanvas->imageInfo(); + pRect->right = canvasSize.width(); + pRect->bottom = canvasSize.height(); + return TRUE; } FX_BOOL CFX_SkiaDeviceDriver::GetDIBits(CFX_DIBitmap* pBitmap, @@ -315,7 +404,8 @@ FX_BOOL CFX_SkiaDeviceDriver::GetDIBits(CFX_DIBitmap* pBitmap, int top, void* pIccTransform, FX_BOOL bDEdge) { - return m_pAggDriver->GetDIBits(pBitmap, left, top, pIccTransform, bDEdge); + return m_pAggDriver && + m_pAggDriver->GetDIBits(pBitmap, left, top, pIccTransform, bDEdge); } FX_BOOL CFX_SkiaDeviceDriver::SetDIBits(const CFX_DIBSource* pBitmap, @@ -326,7 +416,8 @@ FX_BOOL CFX_SkiaDeviceDriver::SetDIBits(const CFX_DIBSource* pBitmap, int blend_type, int alpha_flag, void* pIccTransform) { - return m_pAggDriver->SetDIBits(pBitmap, argb, pSrcRect, left, top, blend_type, + return m_pAggDriver && + m_pAggDriver->SetDIBits(pBitmap, argb, pSrcRect, left, top, blend_type, alpha_flag, pIccTransform); } @@ -341,7 +432,8 @@ FX_BOOL CFX_SkiaDeviceDriver::StretchDIBits(const CFX_DIBSource* pSource, int alpha_flag, void* pIccTransform, int blend_type) { - return m_pAggDriver->StretchDIBits(pSource, argb, dest_left, dest_top, + return m_pAggDriver && + m_pAggDriver->StretchDIBits(pSource, argb, dest_left, dest_top, dest_width, dest_height, pClipRect, flags, alpha_flag, pIccTransform, blend_type); } @@ -355,23 +447,98 @@ FX_BOOL CFX_SkiaDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, int alpha_flag, void* pIccTransform, int blend_type) { - return m_pAggDriver->StartDIBits(pSource, bitmap_alpha, argb, pMatrix, - render_flags, handle, alpha_flag, - pIccTransform, blend_type); + SkColorType colorType; + void* buffer = pSource->GetBuffer(); + std::unique_ptr dst8Storage; + std::unique_ptr dst32Storage; + int width = pSource->GetWidth(); + int height = pSource->GetHeight(); + int rowBytes = pSource->GetPitch(); + switch (pSource->GetBPP()) { + case 1: { + dst8Storage.reset(FX_Alloc2D(uint8_t, width, height)); + uint8_t* dst8Pixels = dst8Storage.get(); + for (int y = 0; y < height; ++y) { + const uint8_t* srcRow = + static_cast(buffer) + y * rowBytes; + uint8_t* dstRow = dst8Pixels + y * width; + for (int x = 0; x < width; ++x) + dstRow[x] = srcRow[x >> 3] & (1 << (~x & 0x07)) ? 0xFF : 0x00; + } + buffer = dst8Storage.get(); + rowBytes = width; + colorType = SkColorType::kGray_8_SkColorType; + } break; + case 24: { + dst32Storage.reset(FX_Alloc2D(uint32_t, width, height)); + uint32_t* dst32Pixels = dst32Storage.get(); + for (int y = 0; y < height; ++y) { + const uint8_t* srcRow = + static_cast(buffer) + y * rowBytes; + uint32_t* dstRow = dst32Pixels + y * width; + for (int x = 0; x < width; ++x) + dstRow[x] = SkPackARGB32(0xFF, srcRow[x * 3 + 2], srcRow[x * 3 + 1], + srcRow[x * 3 + 0]); + } + buffer = dst32Storage.get(); + rowBytes = width * sizeof(uint32_t); + colorType = SkColorType::kN32_SkColorType; + } break; + case 32: + colorType = SkColorType::kN32_SkColorType; + break; + default: + colorType = SkColorType::kUnknown_SkColorType; + } + SkImageInfo imageInfo = + SkImageInfo::Make(width, height, colorType, kOpaque_SkAlphaType); + SkBitmap skBitmap; + skBitmap.installPixels(imageInfo, buffer, rowBytes, + nullptr, /* TODO(caryclark) : set color table */ + nullptr, nullptr); + m_pCanvas->save(); + bool landscape = !pMatrix->a; + if (landscape) + m_pCanvas->translate(m_pCanvas->imageInfo().width(), 0); + else + m_pCanvas->translate(pMatrix->e, pMatrix->f + pMatrix->d); + + SkMatrix skMatrix = SkMatrix::MakeScale(1.f / width, 1.f / height); + m_pCanvas->concat(skMatrix); + const CFX_Matrix& m = *pMatrix; + // note that PDF's y-axis goes up; Skia's y-axis goes down + if (landscape) + skMatrix.setAll(-m.a, -m.b, m.e, m.c, m.d, m.f, 0, 0, 1); + else + skMatrix.setAll(m.a, m.b, 0, -m.c, -m.d, 0, 0, 0, 1); + m_pCanvas->concat(skMatrix); + SkPaint paint; + paint.setAntiAlias(true); + paint.setFilterQuality(kLow_SkFilterQuality); + m_pCanvas->drawBitmap(skBitmap, 0, 0, &paint); + m_pCanvas->restore(); + return TRUE; } FX_BOOL CFX_SkiaDeviceDriver::ContinueDIBits(void* pHandle, IFX_Pause* pPause) { - return m_pAggDriver->ContinueDIBits(pHandle, pPause); + return m_pAggDriver && m_pAggDriver->ContinueDIBits(pHandle, pPause); } void CFX_SkiaDeviceDriver::CancelDIBits(void* pHandle) { - m_pAggDriver->CancelDIBits(pHandle); + if (m_pAggDriver) + m_pAggDriver->CancelDIBits(pHandle); } CFX_SkiaDevice::CFX_SkiaDevice() { m_bOwnedBitmap = FALSE; } +SkPictureRecorder* CFX_SkiaDevice::CreateRecorder(int size_x, int size_y) { + CFX_SkiaDeviceDriver* skDriver = new CFX_SkiaDeviceDriver(size_x, size_y); + SetDeviceDriver(skDriver); + return skDriver->GetRecorder(); +} + FX_BOOL CFX_SkiaDevice::Attach(CFX_DIBitmap* pBitmap, int dither_bits, FX_BOOL bRgbByteOrder, @@ -380,9 +547,15 @@ FX_BOOL CFX_SkiaDevice::Attach(CFX_DIBitmap* pBitmap, if (!pBitmap) return FALSE; SetBitmap(pBitmap); - CFX_SkiaDeviceDriver* pDriver = new CFX_SkiaDeviceDriver( - pBitmap, dither_bits, bRgbByteOrder, pOriDevice, bGroupKnockout); - SetDeviceDriver(pDriver); + SetDeviceDriver(new CFX_SkiaDeviceDriver(pBitmap, dither_bits, bRgbByteOrder, + pOriDevice, bGroupKnockout)); + return TRUE; +} + +FX_BOOL CFX_SkiaDevice::AttachRecorder(SkPictureRecorder* recorder) { + if (!recorder) + return FALSE; + SetDeviceDriver(new CFX_SkiaDeviceDriver(recorder)); return TRUE; } @@ -409,17 +582,4 @@ CFX_SkiaDevice::~CFX_SkiaDevice() { delete GetBitmap(); } -#if 0 -#include -#include - -void SkDebugf(const char format[], ...) { - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} - -#endif - #endif diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h index 1a36fbe85b..bb0a651263 100644 --- a/core/fxge/skia/fx_skia_device.h +++ b/core/fxge/skia/fx_skia_device.h @@ -10,6 +10,7 @@ class SkCanvas; class SkPaint; class SkPath; +class SkPictureRecorder; struct SkIRect; class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { @@ -19,6 +20,8 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { FX_BOOL bRgbByteOrder, CFX_DIBitmap* pOriDevice, FX_BOOL bGroupKnockout); + CFX_SkiaDeviceDriver(SkPictureRecorder* recorder); + CFX_SkiaDeviceDriver(int size_x, int size_y); ~CFX_SkiaDeviceDriver() override; /** Options */ @@ -139,10 +142,13 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { void SetClipMask(SkPath& skPath, SkPaint* spaint); virtual uint8_t* GetBuffer() const { return m_pAggDriver->GetBuffer(); } void PaintStroke(SkPaint* spaint, const CFX_GraphStateData* pGraphState); + SkPictureRecorder* GetRecorder() const { return m_pRecorder; } private: CFX_AggDeviceDriver* m_pAggDriver; - SkCanvas* m_canvas; + SkCanvas* m_pCanvas; + SkPictureRecorder* const m_pRecorder; + int m_ditherBits; }; #endif // defined(_SKIA_SUPPORT_) diff --git a/core/include/fxge/fx_ge.h b/core/include/fxge/fx_ge.h index 8a62f13d14..8e9df8c544 100644 --- a/core/include/fxge/fx_ge.h +++ b/core/include/fxge/fx_ge.h @@ -16,6 +16,7 @@ class CFX_FontCache; class CFX_FaceCache; class IFX_RenderDeviceDriver; class CCodec_ModuleMgr; +class SkPictureRecorder; class CFX_GEModule { public: @@ -427,6 +428,7 @@ class CFX_FxgeDevice : public CFX_RenderDevice { protected: bool m_bOwnedBitmap; }; + class CFX_SkiaDevice : public CFX_RenderDevice { public: CFX_SkiaDevice(); @@ -438,12 +440,16 @@ class CFX_SkiaDevice : public CFX_RenderDevice { CFX_DIBitmap* pOriDevice = NULL, FX_BOOL bGroupKnockout = FALSE); + FX_BOOL AttachRecorder(SkPictureRecorder* recorder); + FX_BOOL Create(int width, int height, FXDIB_Format format, int dither_bits = 0, CFX_DIBitmap* pOriDevice = NULL); + SkPictureRecorder* CreateRecorder(int size_x, int size_y); + protected: FX_BOOL m_bOwnedBitmap; }; diff --git a/fpdfsdk/fpdfformfill.cpp b/fpdfsdk/fpdfformfill.cpp index 31f5732ce5..b33e303db0 100644 --- a/fpdfsdk/fpdfformfill.cpp +++ b/fpdfsdk/fpdfformfill.cpp @@ -287,15 +287,16 @@ DLLEXPORT FPDF_BOOL STDCALL FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) { return pSDKDoc->KillFocusAnnot(0); } -DLLEXPORT void STDCALL FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, - FPDF_BITMAP bitmap, - FPDF_PAGE page, - int start_x, - int start_y, - int size_x, - int size_y, - int rotate, - int flags) { +static void FFLCommon(FPDF_FORMHANDLE hHandle, + FPDF_BITMAP bitmap, + FPDF_RECORDER recorder, + FPDF_PAGE page, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + int flags) { if (!hHandle) return; @@ -336,7 +337,8 @@ DLLEXPORT void STDCALL FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, FX_RECT clip(start_x, start_y, start_x + size_x, start_y + size_y); #ifdef _SKIA_SUPPORT_ - std::unique_ptr pDevice(new CFX_SkiaDevice); + std::unique_ptr pDevice(new CFX_SkiaDevice()); + pDevice->AttachRecorder(static_cast(recorder)); #else std::unique_ptr pDevice(new CFX_FxgeDevice); #endif @@ -374,6 +376,34 @@ DLLEXPORT void STDCALL FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, #endif // PDF_ENABLE_XFA } +DLLEXPORT void STDCALL FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, + FPDF_BITMAP bitmap, + FPDF_PAGE page, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + int flags) { + FFLCommon(hHandle, bitmap, nullptr, page, start_x, start_y, size_x, size_y, + rotate, flags); +} + +#ifdef _SKIA_SUPPORT_ +DLLEXPORT void STDCALL FPDF_FFLRecord(FPDF_FORMHANDLE hHandle, + FPDF_RECORDER recorder, + FPDF_PAGE page, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + int flags) { + FFLCommon(hHandle, nullptr, recorder, page, start_x, start_y, size_x, size_y, + rotate, flags); +} +#endif + #ifdef PDF_ENABLE_XFA DLLEXPORT void STDCALL FPDF_Widget_Undo(FPDF_DOCUMENT document, FPDF_WIDGET hWidget) { diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp index 48fe0664a2..4630ac1e7e 100644 --- a/fpdfsdk/fpdfview.cpp +++ b/fpdfsdk/fpdfview.cpp @@ -645,7 +645,7 @@ DLLEXPORT void STDCALL FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, CRenderContext* pContext = new CRenderContext; pPage->SetPrivateData((void*)1, pContext, DropContext); #ifdef _SKIA_SUPPORT_ - pContext->m_pDevice = new CFX_SkiaDevice; + pContext->m_pDevice = new CFX_SkiaDevice(); if (flags & FPDF_REVERSE_BYTE_ORDER) ((CFX_SkiaDevice*)pContext->m_pDevice) @@ -669,6 +669,26 @@ DLLEXPORT void STDCALL FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, pPage->RemovePrivateData((void*)1); } +#ifdef _SKIA_SUPPORT_ +DLLEXPORT FPDF_RECORDER STDCALL FPDF_RenderPageSkp(FPDF_PAGE page, + int size_x, + int size_y) { + CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!pPage) + return nullptr; + std::unique_ptr pContext(new CRenderContext); + pPage->SetPrivateData((void*)1, pContext.get(), DropContext); + CFX_SkiaDevice* skDevice = new CFX_SkiaDevice(); + FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y); + pContext->m_pDevice = skDevice; + + FPDF_RenderPage_Retail(pContext.get(), page, 0, 0, size_x, size_y, 0, 0, TRUE, + NULL); + pPage->RemovePrivateData((void*)1); + return recorder; +} +#endif + DLLEXPORT void STDCALL FPDF_ClosePage(FPDF_PAGE page) { if (!page) return; diff --git a/public/fpdf_formfill.h b/public/fpdf_formfill.h index 3aeccb92f3..81d6b691f0 100644 --- a/public/fpdf_formfill.h +++ b/public/fpdf_formfill.h @@ -1508,6 +1508,18 @@ DLLEXPORT void STDCALL FPDF_FFLDraw(FPDF_FORMHANDLE hHandle, int rotate, int flags); +#ifdef _SKIA_SUPPORT_ +DLLEXPORT void STDCALL FPDF_FFLRecord(FPDF_FORMHANDLE hHandle, + FPDF_RECORDER recorder, + FPDF_PAGE page, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + int flags); +#endif + #ifdef PDF_ENABLE_XFA /** * Function: FPDF_HasXFAField diff --git a/public/fpdfview.h b/public/fpdfview.h index 68d0b1ad83..dad6299da5 100644 --- a/public/fpdfview.h +++ b/public/fpdfview.h @@ -37,6 +37,7 @@ typedef void* FPDF_PAGELINK; typedef void* FPDF_PAGEOBJECT; // Page object(text, path, etc) typedef void* FPDF_PAGERANGE; typedef void* FPDF_PATH; +typedef void* FPDF_RECORDER; typedef void* FPDF_SCHHANDLE; typedef void* FPDF_TEXTPAGE; @@ -582,6 +583,12 @@ DLLEXPORT void STDCALL FPDF_RenderPageBitmap(FPDF_BITMAP bitmap, int rotate, int flags); +#ifdef _SKIA_SUPPORT_ +DLLEXPORT FPDF_RECORDER STDCALL FPDF_RenderPageSkp(FPDF_PAGE page, + int size_x, + int size_y); +#endif + // Function: FPDF_ClosePage // Close a loaded PDF page. // Parameters: diff --git a/samples/BUILD.gn b/samples/BUILD.gn index 9ff1bea4c1..5e300158c9 100644 --- a/samples/BUILD.gn +++ b/samples/BUILD.gn @@ -9,8 +9,8 @@ import("../pdfium.gni") group("samples") { testonly = true deps = [ - ":pdfium_test", ":pdfium_diff", + ":pdfium_test", ] } @@ -26,6 +26,9 @@ config("pdfium_samples_config") { if (pdf_enable_xfa) { defines += [ "PDF_ENABLE_XFA" ] } + if (pdf_use_skia) { + defines += [ "PDF_ENABLE_SKIA" ] + } } executable("pdfium_test") { @@ -56,6 +59,9 @@ executable("pdfium_test") { ] configs += [ "//v8:external_startup_data" ] } + if (pdf_use_skia) { + deps += [ "//skia" ] + } configs += [ ":pdfium_samples_config" ] } diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc index 656e041e36..2ca31b08e9 100644 --- a/samples/pdfium_test.cc +++ b/samples/pdfium_test.cc @@ -13,6 +13,10 @@ #include #include +#if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_ +#define _SKIA_SUPPORT_ +#endif + #include "public/fpdf_dataavail.h" #include "public/fpdf_edit.h" #include "public/fpdf_ext.h" @@ -31,6 +35,11 @@ #define snprintf _snprintf #endif +#ifdef PDF_ENABLE_SKIA +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkStream.h" +#endif + enum OutputFormat { OUTPUT_NONE, OUTPUT_PPM, @@ -39,6 +48,9 @@ enum OutputFormat { OUTPUT_BMP, OUTPUT_EMF, #endif +#ifdef PDF_ENABLE_SKIA + OUTPUT_SKP, +#endif }; struct Options { @@ -116,7 +128,7 @@ static void WritePng(const char* pdf_name, int num, const void* buffer_void, filename, sizeof(filename), "%s.%d.png", pdf_name, num); if (chars_formatted < 0 || static_cast(chars_formatted) >= sizeof(filename)) { - fprintf(stderr, "Filname %s is too long\n", filename); + fprintf(stderr, "Filename %s is too long\n", filename); return; } @@ -195,6 +207,25 @@ void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) { } #endif +#ifdef PDF_ENABLE_SKIA +void WriteSkp(const char* pdf_name, int num, const void* recorder) { + char filename[256]; + int chars_formatted = + snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num); + + if (chars_formatted < 0 || + static_cast(chars_formatted) >= sizeof(filename)) { + fprintf(stderr, "Filename %s is too long\n", filename); + return; + } + + SkPictureRecorder* r = (SkPictureRecorder*)recorder; + SkPicture* picture = r->endRecordingAsPicture(); + SkFILEWStream wStream(filename); + picture->serialize(&wStream); +} +#endif + // These example JS platform callback handlers are entirely optional, // and exist here to show the flow of information from a document back // to the embedder. @@ -319,6 +350,14 @@ bool ParseCommandLine(const std::vector& args, return false; } options->output_format = OUTPUT_PNG; +#ifdef PDF_ENABLE_SKIA + } else if (cur_arg == "--skp") { + if (options->output_format != OUTPUT_NONE) { + fprintf(stderr, "Duplicate or conflicting --skp argument\n"); + return false; + } + options->output_format = OUTPUT_SKP; +#endif } else if (cur_arg.size() > 11 && cur_arg.compare(0, 11, "--font-dir=") == 0) { if (!options->font_directory.empty()) { @@ -431,6 +470,14 @@ bool RenderPage(const std::string& name, WritePpm(name.c_str(), page_index, buffer, stride, width, height); break; +#ifdef PDF_ENABLE_SKIA + case OUTPUT_SKP: { + std::unique_ptr recorder( + (SkPictureRecorder*)FPDF_RenderPageSkp(page, width, height)); + FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0); + WriteSkp(name.c_str(), page_index, recorder.get()); + } break; +#endif default: break; } @@ -634,7 +681,10 @@ static const char usage_string[] = " --emf - write page meta files ..emf\n" #endif // _WIN32 " --png - write page images ..png\n" - " --ppm - write page images ..ppm\n"; +#ifdef PDF_ENABLE_SKIA + " --skp - write page images ..skp\n" +#endif + ""; int main(int argc, const char* argv[]) { std::vector args(argv, argv + argc); diff --git a/samples/samples.gyp b/samples/samples.gyp index 6f262d5a04..b345f1d2c3 100644 --- a/samples/samples.gyp +++ b/samples/samples.gyp @@ -66,6 +66,12 @@ '<(DEPTH)/v8/tools/gyp/v8.gyp:v8_libplatform', ], }], + ['pdf_use_skia==1', { + 'defines': ['PDF_ENABLE_SKIA'], + 'dependencies': [ + '<(DEPTH)/skia/skia.gyp:skia', + ], + }], ], }, { -- cgit v1.2.3