blob: ba06a1f3c273e4b9f9ea77c455f0bb0d4d4b0562 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkWindow.h"
#include "SkCanvas.h"
#include "SkOSMenu.h"
#include "SkSurface.h"
#include "SkSystemEventTypes.h"
#include "SkTime.h"
#define SK_EventDelayInval "\xd" "n" "\xa" "l"
SkWindow::SkWindow()
: fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)
, fFocusView(nullptr)
{
fClicks.reset();
fWaitingOnInval = false;
fMatrix.reset();
fBitmap.allocN32Pixels(0, 0);
}
SkWindow::~SkWindow() {
fClicks.deleteAll();
fMenus.deleteAll();
}
sk_sp<SkSurface> SkWindow::makeSurface() {
const SkBitmap& bm = this->getBitmap();
return SkSurface::MakeRasterDirect(bm.info(), bm.getPixels(), bm.rowBytes(), &fSurfaceProps);
}
void SkWindow::setMatrix(const SkMatrix& matrix) {
if (fMatrix != matrix) {
fMatrix = matrix;
this->inval(nullptr);
}
}
void SkWindow::preConcat(const SkMatrix& matrix) {
SkMatrix m;
m.setConcat(fMatrix, matrix);
this->setMatrix(m);
}
void SkWindow::postConcat(const SkMatrix& matrix) {
SkMatrix m;
m.setConcat(matrix, fMatrix);
this->setMatrix(m);
}
void SkWindow::resize(const SkImageInfo& info) {
if (fBitmap.info() != info) {
fBitmap.allocPixels(info);
this->inval(nullptr);
}
this->setSize(SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height()));
}
void SkWindow::resize(int width, int height) {
this->resize(fBitmap.info().makeWH(width, height));
}
void SkWindow::setColorType(SkColorType ct, sk_sp<SkColorSpace> cs) {
const SkImageInfo& info = fBitmap.info();
this->resize(SkImageInfo::Make(info.width(), info.height(), ct, kPremul_SkAlphaType, cs));
}
bool SkWindow::handleInval(const SkRect* localR) {
SkIRect ir;
if (localR) {
SkRect devR;
SkMatrix inverse;
if (!fMatrix.invert(&inverse)) {
return false;
}
fMatrix.mapRect(&devR, *localR);
devR.round(&ir);
} else {
ir.set(0, 0,
SkScalarRoundToInt(this->width()),
SkScalarRoundToInt(this->height()));
}
fDirtyRgn.op(ir, SkRegion::kUnion_Op);
this->onHandleInval(ir);
return true;
}
void SkWindow::forceInvalAll() {
fDirtyRgn.setRect(0, 0,
SkScalarCeilToInt(this->width()),
SkScalarCeilToInt(this->height()));
}
#ifdef SK_SIMULATE_FAILED_MALLOC
extern bool gEnableControlledThrow;
#endif
bool SkWindow::update(SkIRect* updateArea) {
if (!fDirtyRgn.isEmpty()) {
sk_sp<SkSurface> surface(this->makeSurface());
SkCanvas* canvas = surface->getCanvas();
canvas->clipRegion(fDirtyRgn);
if (updateArea) {
*updateArea = fDirtyRgn.getBounds();
}
SkAutoCanvasRestore acr(canvas, true);
canvas->concat(fMatrix);
// empty this now, so we can correctly record any inval calls that
// might be made during the draw call.
fDirtyRgn.setEmpty();
#ifdef SK_SIMULATE_FAILED_MALLOC
gEnableControlledThrow = true;
#endif
#ifdef SK_BUILD_FOR_WIN32
//try {
this->draw(canvas);
//}
//catch (...) {
//}
#else
this->draw(canvas);
#endif
#ifdef SK_SIMULATE_FAILED_MALLOC
gEnableControlledThrow = false;
#endif
return true;
}
return false;
}
bool SkWindow::handleChar(SkUnichar uni) {
if (this->onHandleChar(uni))
return true;
SkView* focus = this->getFocusView();
if (focus == nullptr)
focus = this;
SkEvent evt(SK_EventType_Unichar);
evt.setFast32(uni);
return focus->doEvent(evt);
}
bool SkWindow::handleKey(SkKey key) {
if (key == kNONE_SkKey)
return false;
if (this->onHandleKey(key))
return true;
// send an event to the focus-view
{
SkView* focus = this->getFocusView();
if (focus == nullptr)
focus = this;
SkEvent evt(SK_EventType_Key);
evt.setFast32(key);
if (focus->doEvent(evt))
return true;
}
if (key == kUp_SkKey || key == kDown_SkKey) {
if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == nullptr)
this->onSetFocusView(nullptr);
return true;
}
return false;
}
bool SkWindow::handleKeyUp(SkKey key) {
if (key == kNONE_SkKey)
return false;
if (this->onHandleKeyUp(key))
return true;
//send an event to the focus-view
{
SkView* focus = this->getFocusView();
if (focus == nullptr)
focus = this;
//should this one be the same?
SkEvent evt(SK_EventType_KeyUp);
evt.setFast32(key);
if (focus->doEvent(evt))
return true;
}
return false;
}
void SkWindow::addMenu(SkOSMenu* menu) {
*fMenus.append() = menu;
this->onAddMenu(menu);
}
void SkWindow::setTitle(const char title[]) {
if (nullptr == title) {
title = "";
}
fTitle.set(title);
this->onSetTitle(title);
}
bool SkWindow::onEvent(const SkEvent& evt) {
if (evt.isType(SK_EventDelayInval)) {
for (SkRegion::Iterator iter(fDirtyRgn); !iter.done(); iter.next())
this->onHandleInval(iter.rect());
fWaitingOnInval = false;
return true;
}
return this->INHERITED::onEvent(evt);
}
bool SkWindow::onGetFocusView(SkView** focus) const {
if (focus)
*focus = fFocusView;
return true;
}
bool SkWindow::onSetFocusView(SkView* focus) {
if (fFocusView != focus) {
if (fFocusView)
fFocusView->onFocusChange(false);
fFocusView = focus;
if (focus)
focus->onFocusChange(true);
}
return true;
}
void SkWindow::onHandleInval(const SkIRect&) {
}
bool SkWindow::onHandleChar(SkUnichar) {
return false;
}
bool SkWindow::onHandleKey(SkKey) {
return false;
}
bool SkWindow::onHandleKeyUp(SkKey) {
return false;
}
bool SkWindow::handleClick(int x, int y, Click::State state, void *owner,
unsigned modifierKeys) {
return this->onDispatchClick(x, y, state, owner, modifierKeys);
}
bool SkWindow::onDispatchClick(int x, int y, Click::State state,
void* owner, unsigned modifierKeys) {
bool handled = false;
// First, attempt to find an existing click with this owner.
int index = -1;
for (int i = 0; i < fClicks.count(); i++) {
if (owner == fClicks[i]->fOwner) {
index = i;
break;
}
}
switch (state) {
case Click::kDown_State: {
if (index != -1) {
delete fClicks[index];
fClicks.remove(index);
}
Click* click = this->findClickHandler(SkIntToScalar(x),
SkIntToScalar(y), modifierKeys);
if (click) {
click->fOwner = owner;
*fClicks.append() = click;
SkView::DoClickDown(click, x, y, modifierKeys);
handled = true;
}
break;
}
case Click::kMoved_State:
if (index != -1) {
SkView::DoClickMoved(fClicks[index], x, y, modifierKeys);
handled = true;
}
break;
case Click::kUp_State:
if (index != -1) {
SkView::DoClickUp(fClicks[index], x, y, modifierKeys);
delete fClicks[index];
fClicks.remove(index);
handled = true;
}
break;
default:
// Do nothing
break;
}
return handled;
}
#if SK_SUPPORT_GPU
#include "GrBackendSurface.h"
#include "GrContext.h"
#include "gl/GrGLInterface.h"
#include "gl/GrGLUtil.h"
#include "SkGr.h"
sk_sp<SkSurface> SkWindow::makeGpuBackedSurface(const AttachmentInfo& attachmentInfo,
const GrGLInterface* interface,
GrContext* grContext) {
int width = SkScalarRoundToInt(this->width());
int height = SkScalarRoundToInt(this->height());
if (0 == width || 0 == height) {
return nullptr;
}
// TODO: Query the actual framebuffer for sRGB capable. However, to
// preserve old (fake-linear) behavior, we don't do this. Instead, rely
// on the flag (currently driven via 'C' mode in SampleApp).
//
// Also, we may not have real sRGB support (ANGLE, in particular), so check for
// that, and fall back to L32:
//
// ... and, if we're using a 10-bit/channel FB0, it doesn't do sRGB conversion on write,
// so pretend that it's non-sRGB 8888:
GrPixelConfig config = grContext->caps()->srgbSupport() &&
info().colorSpace() &&
(attachmentInfo.fColorBits != 30)
? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
GrGLFramebufferInfo fbInfo;
GrGLint buffer;
GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer);
fbInfo.fFBOID = buffer;
GrBackendRenderTarget backendRT(width,
height,
attachmentInfo.fSampleCount,
attachmentInfo.fStencilBits,
config,
fbInfo);
sk_sp<SkColorSpace> colorSpace =
grContext->caps()->srgbSupport() && info().colorSpace()
? SkColorSpace::MakeSRGB() : nullptr;
return SkSurface::MakeFromBackendRenderTarget(grContext, backendRT, kBottomLeft_GrSurfaceOrigin,
colorSpace, &fSurfaceProps);
}
#endif