Add support to render the background directly on screen

Renderer::renderCheckeredBackground() function was used only for the
FullscreenPreviewCommand, but now we use
ShaderRenderer::renderCheckeredBackground() to render the background
in the Editor too. So the sprite is painted in a backbuffer and then
composited with the already painted background using
Graphics::drawSurface().
This commit is contained in:
David Capello 2022-09-20 19:28:17 -03:00
parent 3c099dc4dc
commit 8c7b94a934
11 changed files with 215 additions and 130 deletions

View File

@ -28,9 +28,11 @@
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "gfx/matrix.h"
#include "os/surface.h"
#include "os/system.h"
#include <cmath>
#include <cstring>
#define PREVIEW_TILED 1
@ -52,14 +54,7 @@ public:
, m_sprite(editor->sprite())
, m_pal(m_sprite->palette(editor->frame()))
, m_proj(editor->projection())
, m_index_bg_color(-1)
, m_doublebuf(Image::create(
IMAGE_RGB,
editor->display()->size().w,
editor->display()->size().h))
, m_doublesur(os::instance()->makeRgbaSurface(
editor->display()->size().w,
editor->display()->size().h)) {
, m_index_bg_color(-1) {
// Do not use DocWriter (do not lock the document) because we
// will call other sub-commands (e.g. previous frame, next frame,
// etc.).
@ -133,8 +128,8 @@ protected:
command->id() == CommandId::GotoNextFrame() ||
command->id() == CommandId::GotoLastFrame())) {
m_context->executeCommand(command, params);
m_repaint = true; // Re-render
invalidate();
m_render.reset(nullptr); // Re-render
}
#if 0
// Play the animation
@ -177,71 +172,100 @@ protected:
}
virtual void onPaint(PaintEvent& ev) override {
gfx::Size displaySize = display()->size();
Graphics* g = ev.graphics();
EditorRender& render = m_editor->renderEngine();
render.setRefLayersVisiblity(false);
render.setProjection(render::Projection());
render.disableOnionskin();
render.setTransparentBackground();
// Render sprite and leave the result in 'm_render' variable
if (m_render == nullptr) {
ImageBufferPtr buf = render.getRenderImageBuffer();
m_render.reset(Image::create(IMAGE_RGB,
m_sprite->width(), m_sprite->height(), buf));
render.renderSprite(
m_render.get(), m_sprite, m_editor->frame());
m_render = os::instance()->makeRgbaSurface(m_sprite->width(),
m_sprite->height());
m_repaint = true;
}
int x, y, w, h, u, v;
x = m_pos.x + m_proj.applyX(m_proj.removeX(m_delta.x));
y = m_pos.y + m_proj.applyY(m_proj.removeY(m_delta.y));
if (m_repaint) {
m_repaint = false;
m_render->clear();
render.setProjection(render::Projection());
render.renderSprite(
m_render.get(), m_sprite, m_editor->frame(),
gfx::ClipF(0, 0, 0, 0, m_sprite->width(), m_sprite->height()));
}
float x, y, w, h, u, v;
x = m_pos.x + m_delta.x;
y = m_pos.y + m_delta.y;
w = m_proj.applyX(m_sprite->width());
h = m_proj.applyY(m_sprite->height());
if (int(m_tiled) & int(TiledMode::X_AXIS)) x = SGN(x) * (ABS(x)%w);
if (int(m_tiled) & int(TiledMode::Y_AXIS)) y = SGN(y) * (ABS(y)%h);
if (int(m_tiled) & int(TiledMode::X_AXIS)) x = SGN(x) * std::fmod(ABS(x), w);
if (int(m_tiled) & int(TiledMode::Y_AXIS)) y = SGN(y) * std::fmod(ABS(y), h);
render.setProjection(m_proj);
if (m_index_bg_color == -1) {
render.setupBackground(m_doc, m_doublebuf->pixelFormat());
render.setProjection(m_proj);
render.setupBackground(m_doc, IMAGE_RGB);
render.renderCheckeredBackground(
m_doublebuf.get(),
gfx::Clip(0, 0, -m_pos.x, -m_pos.y,
m_doublebuf->width(), m_doublebuf->height()));
g->getInternalSurface(), m_sprite,
gfx::Clip(g->getInternalDeltaX(),
g->getInternalDeltaY(), clientBounds()));
// Invalidate the whole Graphics (as we've just modified its
// internal os::Surface directly).
g->invalidate(g->getClipBounds());
}
else {
doc::clear_image(m_doublebuf.get(), m_pal->getEntry(m_index_bg_color));
auto col = m_pal->getEntry(m_index_bg_color);
g->fillRect(gfx::rgba(doc::rgba_getr(col),
doc::rgba_getg(col),
doc::rgba_getb(col),
doc::rgba_geta(col)),
clientBounds());
}
const double sx = m_proj.scaleX();
const double sy = m_proj.scaleY();
gfx::RectF tiledBounds;
tiledBounds.w = (2+std::ceil(float(clientBounds().w)/w))*w;
tiledBounds.h = (2+std::ceil(float(clientBounds().h)/h))*h;
tiledBounds.x = x-w;
tiledBounds.y = y-h;
g->save();
switch (m_tiled) {
case TiledMode::NONE:
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, x, y,
255, doc::BlendMode::NORMAL);
g->setMatrix(gfx::Matrix::MakeTrans(x, y));
g->concat(gfx::Matrix::MakeScale(sx, sy));
g->drawRgbaSurface(m_render.get(), 0, 0);
break;
case TiledMode::X_AXIS:
for (u=x-w; u<displaySize.w+w; u+=w)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, u, y,
255, doc::BlendMode::NORMAL);
for (u=tiledBounds.x; u<tiledBounds.x2(); u+=w) {
g->setMatrix(gfx::Matrix::MakeTrans(u, y));
g->concat(gfx::Matrix::MakeScale(sx, sy));
g->drawRgbaSurface(m_render.get(), 0, 0);
}
break;
case TiledMode::Y_AXIS:
for (v=y-h; v<displaySize.h+h; v+=h)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, x, v,
255, doc::BlendMode::NORMAL);
for (v=tiledBounds.y; v<tiledBounds.y2(); v+=h) {
g->setMatrix(gfx::Matrix::MakeTrans(x, v));
g->concat(gfx::Matrix::MakeScale(sx, sy));
g->drawRgbaSurface(m_render.get(), 0, 0);
}
break;
case TiledMode::BOTH:
for (v=y-h; v<displaySize.h+h; v+=h)
for (u=x-w; u<displaySize.w+w; u+=w)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, u, v,
255, doc::BlendMode::NORMAL);
for (v=tiledBounds.y; v<tiledBounds.y2(); v+=h) {
for (u=tiledBounds.x; u<tiledBounds.x2(); u+=w) {
g->setMatrix(gfx::Matrix::MakeTrans(u, v));
g->concat(gfx::Matrix::MakeScale(sx, sy));
g->drawRgbaSurface(m_render.get(), 0, 0);
}
}
break;
}
convert_image_to_surface(m_doublebuf.get(), m_pal,
m_doublesur.get(), 0, 0, 0, 0, m_doublebuf->width(), m_doublebuf->height());
g->blit(m_doublesur.get(), 0, 0, 0, 0, m_doublesur->width(), m_doublesur->height());
g->restore();
}
private:
@ -255,9 +279,8 @@ private:
gfx::Point m_delta;
render::Projection m_proj;
int m_index_bg_color;
std::unique_ptr<Image> m_render;
std::unique_ptr<Image> m_doublebuf;
os::SurfaceRef m_doublesur;
os::SurfaceRef m_render;
bool m_repaint = true;
filters::TiledMode m_tiled;
};

View File

@ -26,16 +26,38 @@ namespace app {
// using render::Render).
class Renderer {
public:
struct Properties {
// True if the background should be rendered first with a
// renderCheckeredBackground() call (in other case
// renderSprite() will render the background too). This allows
// to draw a background directly on the screen and then the
// sprite in a backbuffer.
bool renderBgOnScreen = false;
// True if the backbuffer should has alpha channel, i.e. to
// composite the background already painted on the screen with
// the sprite painted in the backbuffer.
bool requiresRgbaBackbuffer = false;
};
virtual ~Renderer() { }
// Returns properties of the renderer (modifies how the editor
// uses the renderer).
virtual const Properties& properties() const = 0;
// ----------------------------------------------------------------------
// Basic configuration
virtual void setRefLayersVisiblity(const bool visible) = 0;
virtual void setNonactiveLayersOpacity(const int opacity) = 0;
virtual void setNewBlendMethod(const bool newBlend) = 0;
virtual void setBgOptions(const render::BgOptions& bg) = 0;
virtual void setProjection(const render::Projection& projection) = 0;
// ----------------------------------------------------------------------
// Advance configuration (for preview/brushes purposes)
virtual void setSelectedLayer(const doc::Layer* layer) = 0;
virtual void setPreviewImage(const doc::Layer* layer,
const doc::frame_t frame,
@ -54,15 +76,15 @@ namespace app {
virtual void setOnionskin(const render::OnionskinOptions& options) = 0;
virtual void disableOnionskin() = 0;
// ----------------------------------------------------------------------
// Compositing
virtual void renderSprite(doc::Image* dstImage,
const doc::Sprite* sprite,
const doc::frame_t frame) = 0;
virtual void renderSprite(os::Surface* dstSurface,
const doc::Sprite* sprite,
const doc::frame_t frame,
const gfx::ClipF& area) = 0;
virtual void renderCheckeredBackground(doc::Image* dstImage,
virtual void renderCheckeredBackground(os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area) = 0;
virtual void renderImage(doc::Image* dstImage,
const doc::Image* srcImage,

View File

@ -24,12 +24,11 @@ namespace app {
namespace {
const char* kBgShaderCode = R"(
uniform half3 iRes, iCanvas, iSrcPos;
uniform half4 iBg1, iBg2;
uniform half2 iStripeSize;
half4 main(vec2 fragcoord) {
vec2 u = (iSrcPos.xy+fragcoord.xy) / iStripeSize.xy;
vec2 u = fragcoord.xy / iStripeSize.xy;
return (mod(mod(floor(u.x), 2) + mod(floor(u.y), 2), 2) != 0.0 ? iBg2: iBg1);
}
)";
@ -63,6 +62,9 @@ inline SkBlendMode to_skia(const doc::BlendMode bm) {
ShaderRenderer::ShaderRenderer()
{
m_properties.renderBgOnScreen = true;
m_properties.requiresRgbaBackbuffer = true;
auto result = SkRuntimeEffect::MakeForShader(SkString(kBgShaderCode));
if (!result.errorText.isEmpty()) {
LOG(ERROR, "Shader error: %s\n", result.errorText.c_str());
@ -96,7 +98,7 @@ void ShaderRenderer::setBgOptions(const render::BgOptions& bg)
void ShaderRenderer::setProjection(const render::Projection& projection)
{
// TODO impl
m_proj = projection;
}
void ShaderRenderer::setSelectedLayer(const doc::Layer* layer)
@ -144,44 +146,25 @@ void ShaderRenderer::disableOnionskin()
// TODO impl
}
void ShaderRenderer::renderSprite(doc::Image* dstImage,
const doc::Sprite* sprite,
const doc::frame_t frame)
{
// TODO impl
}
void ShaderRenderer::renderSprite(os::Surface* dstSurface,
const doc::Sprite* sprite,
const doc::frame_t frame,
const gfx::ClipF& area)
{
SkRuntimeShaderBuilder builder(m_bgEffect);
builder.uniform("iRes") = SkV3{float(area.size.w), float(area.size.h), 0.0f};
builder.uniform("iCanvas") = SkV3{float(sprite->width()), float(sprite->height()), 0.0f};
builder.uniform("iSrcPos") = SkV3{float(area.src.x), float(area.src.y), 0.0f};
builder.uniform("iBg1") = gfxColor_to_SkV4(
color_utils::color_for_ui(
app::Color::fromImage(sprite->pixelFormat(),
m_bgOptions.color1)));
builder.uniform("iBg2") = gfxColor_to_SkV4(
color_utils::color_for_ui(
app::Color::fromImage(sprite->pixelFormat(),
m_bgOptions.color2)));
builder.uniform("iStripeSize") = SkV2{
float(m_bgOptions.stripeSize.w),
float(m_bgOptions.stripeSize.h)};
SkCanvas* canvas = &static_cast<os::SkiaSurface*>(dstSurface)->canvas();
canvas->save();
{
SkPaint p;
p.setStyle(SkPaint::kFill_Style);
p.setShader(builder.makeShader());
p.setColor(SK_ColorTRANSPARENT);
p.setBlendMode(SkBlendMode::kSrc);
canvas->drawRect(SkRect::MakeXYWH(area.dst.x, area.dst.y, area.size.w, area.size.h), p);
// Draw cels
canvas->translate(area.dst.x - area.src.x,
area.dst.y - area.src.y);
canvas->scale(m_proj.scaleX(), m_proj.scaleY());
drawLayerGroup(canvas, sprite, sprite->root(), frame, area);
}
canvas->restore();
@ -225,8 +208,8 @@ void ShaderRenderer::drawLayerGroup(SkCanvas* canvas,
p.setAlpha(opacity);
p.setBlendMode(to_skia(imgLayer->blendMode()));
canvas->drawImage(skImg.get(),
SkIntToScalar(area.dst.x + cel->x() - area.src.x),
SkIntToScalar(area.dst.y + cel->y() - area.src.y),
SkIntToScalar(cel->x()),
SkIntToScalar(cel->y()),
SkSamplingOptions(),
&p);
}
@ -247,10 +230,41 @@ void ShaderRenderer::drawLayerGroup(SkCanvas* canvas,
}
}
void ShaderRenderer::renderCheckeredBackground(doc::Image* dstImage,
void ShaderRenderer::renderCheckeredBackground(os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area)
{
// TODO impl
SkRuntimeShaderBuilder builder(m_bgEffect);
builder.uniform("iBg1") = gfxColor_to_SkV4(
color_utils::color_for_ui(
app::Color::fromImage(sprite->pixelFormat(),
m_bgOptions.color1)));
builder.uniform("iBg2") = gfxColor_to_SkV4(
color_utils::color_for_ui(
app::Color::fromImage(sprite->pixelFormat(),
m_bgOptions.color2)));
float sx = (m_bgOptions.zoom ? m_proj.scaleX(): 1.0);
float sy = (m_bgOptions.zoom ? m_proj.scaleY(): 1.0);
builder.uniform("iStripeSize") = SkV2{
float(m_bgOptions.stripeSize.w) * sx,
float(m_bgOptions.stripeSize.h) * sy};
SkCanvas* canvas = &static_cast<os::SkiaSurface*>(dstSurface)->canvas();
canvas->save();
{
SkPaint p;
p.setStyle(SkPaint::kFill_Style);
p.setShader(builder.makeShader());
canvas->translate(
SkIntToScalar(area.dst.x - area.src.x),
SkIntToScalar(area.dst.y - area.src.y));
canvas->drawRect(
SkRect::MakeXYWH(area.src.x, area.src.y, area.size.w, area.size.h), p);
}
canvas->restore();
}
void ShaderRenderer::renderImage(doc::Image* dstImage,

View File

@ -26,6 +26,8 @@ namespace app {
ShaderRenderer();
~ShaderRenderer();
const Properties& properties() const override { return m_properties; }
void setRefLayersVisiblity(const bool visible) override;
void setNonactiveLayersOpacity(const int opacity) override;
void setNewBlendMethod(const bool newBlend) override;
@ -50,14 +52,12 @@ namespace app {
void setOnionskin(const render::OnionskinOptions& options) override;
void disableOnionskin() override;
void renderSprite(doc::Image* dstImage,
const doc::Sprite* sprite,
const doc::frame_t frame) override;
void renderSprite(os::Surface* dstSurface,
const doc::Sprite* sprite,
const doc::frame_t frame,
const gfx::ClipF& area) override;
void renderCheckeredBackground(doc::Image* dstImage,
void renderCheckeredBackground(os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area) override;
void renderImage(doc::Image* dstImage,
const doc::Image* srcImage,
@ -74,7 +74,9 @@ namespace app {
const doc::frame_t frame,
const gfx::ClipF& area);
Properties m_properties;
render::BgOptions m_bgOptions;
render::Projection m_proj;
sk_sp<SkRuntimeEffect> m_bgEffect;
};

View File

@ -89,13 +89,6 @@ void SimpleRenderer::disableOnionskin()
m_render.disableOnionskin();
}
void SimpleRenderer::renderSprite(doc::Image* dstImage,
const doc::Sprite* sprite,
const doc::frame_t frame)
{
m_render.renderSprite(dstImage, sprite, frame);
}
void SimpleRenderer::renderSprite(os::Surface* dstSurface,
const doc::Sprite* sprite,
const doc::frame_t frame,
@ -110,10 +103,18 @@ void SimpleRenderer::renderSprite(os::Surface* dstSurface,
dstSurface, 0, 0, 0, 0, area.size.w, area.size.h);
}
void SimpleRenderer::renderCheckeredBackground(doc::Image* dstImage,
void SimpleRenderer::renderCheckeredBackground(os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area)
{
m_render.renderCheckeredBackground(dstImage, area);
ImageRef dstImage(Image::create(
IMAGE_RGB, area.size.w, area.size.h,
EditorRender::getRenderImageBuffer()));
m_render.renderCheckeredBackground(dstImage.get(), area);
convert_image_to_surface(dstImage.get(), sprite->palette(0),
dstSurface, 0, 0, 0, 0, area.size.w, area.size.h);
}
void SimpleRenderer::renderImage(doc::Image* dstImage,

View File

@ -17,6 +17,8 @@ namespace app {
// CPU-only.
class SimpleRenderer : public Renderer {
public:
const Properties& properties() const override { return m_properties; }
void setRefLayersVisiblity(const bool visible) override;
void setNonactiveLayersOpacity(const int opacity) override;
void setNewBlendMethod(const bool newBlend) override;
@ -41,14 +43,12 @@ namespace app {
void setOnionskin(const render::OnionskinOptions& options) override;
void disableOnionskin() override;
void renderSprite(doc::Image* dstImage,
const doc::Sprite* sprite,
const doc::frame_t frame) override;
void renderSprite(os::Surface* dstSurface,
const doc::Sprite* sprite,
const doc::frame_t frame,
const gfx::ClipF& area) override;
void renderCheckeredBackground(doc::Image* dstImage,
void renderCheckeredBackground(os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area) override;
void renderImage(doc::Image* dstImage,
const doc::Image* srcImage,
@ -58,6 +58,7 @@ namespace app {
const int opacity,
const doc::BlendMode blendMode) override;
private:
Properties m_properties;
render::Render m_render;
};

View File

@ -662,23 +662,13 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
// Convert the render to a os::Surface
static os::SurfaceRef rendered = nullptr; // TODO move this to other centralized place
const auto& renderProperties = m_renderEngine->properties();
try {
// Generate a "expose sprite pixels" notification. This is used by
// tool managers that need to validate this region (copy pixels from
// the original cel) before it can be used by the RenderEngine.
m_document->notifyExposeSpritePixels(m_sprite, gfx::Region(expose));
// Create a temporary surface to draw the sprite on it
if (!rendered ||
rendered->width() < rc2.w ||
rendered->height() < rc2.h ||
rendered->colorSpace() != m_document->osColorSpace()) {
const int maxw = std::max(rc2.w, rendered ? rendered->width(): 0);
const int maxh = std::max(rc2.h, rendered ? rendered->height(): 0);
rendered = os::instance()->makeSurface(
maxw, maxh, m_document->osColorSpace());
}
m_renderEngine->setNewBlendMethod(pref.experimental.newBlend());
m_renderEngine->setRefLayersVisiblity(true);
m_renderEngine->setSelectedLayer(m_layer);
@ -686,8 +676,6 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
m_renderEngine->setNonactiveLayersOpacity(pref.experimental.nonactiveLayersOpacity());
else
m_renderEngine->setNonactiveLayersOpacity(255);
m_renderEngine->setProjection(
newEngine ? render::Projection(): m_proj);
m_renderEngine->setupBackground(m_document, IMAGE_RGB);
m_renderEngine->disableOnionskin();
@ -727,6 +715,32 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
m_layer, m_frame);
}
// Render background first (e.g. new ShaderRenderer will paint the
// background on the screen first and then composite the rendered
// sprite on it.)
if (renderProperties.renderBgOnScreen) {
m_renderEngine->setProjection(m_proj);
m_renderEngine->renderCheckeredBackground(
g->getInternalSurface(),
m_sprite,
gfx::Clip(dest.x + g->getInternalDeltaX(),
dest.y + g->getInternalDeltaY(),
m_proj.apply(rc2)));
}
// Create a temporary surface to draw the sprite on it
if (!rendered ||
rendered->width() < rc2.w ||
rendered->height() < rc2.h ||
rendered->colorSpace() != m_document->osColorSpace()) {
const int maxw = std::max(rc2.w, rendered ? rendered->width(): 0);
const int maxh = std::max(rc2.h, rendered ? rendered->height(): 0);
rendered = os::instance()->makeRgbaSurface(
maxw, maxh, m_document->osColorSpace());
}
m_renderEngine->setProjection(
newEngine ? render::Projection(): m_proj);
m_renderEngine->renderSprite(
rendered.get(), m_sprite, m_frame, gfx::Clip(0, 0, rc2));
@ -763,11 +777,17 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
}
}
os::Paint p;
if (renderProperties.requiresRgbaBackbuffer)
p.blendMode(os::BlendMode::SrcOver);
else
p.blendMode(os::BlendMode::Src);
g->drawSurface(rendered.get(),
gfx::Rect(0, 0, rc2.w, rc2.h),
dest,
sampling,
nullptr);
&p);
}
else {
g->blit(rendered.get(), 0, 0, dest.x, dest.y, dest.w, dest.h);

View File

@ -175,14 +175,6 @@ void EditorRender::disableOnionskin()
m_renderer->disableOnionskin();
}
void EditorRender::renderSprite(
doc::Image* dstImage,
const doc::Sprite* sprite,
doc::frame_t frame)
{
m_renderer->renderSprite(dstImage, sprite, frame);
}
void EditorRender::renderSprite(
os::Surface* dstSurface,
const doc::Sprite* sprite,
@ -193,10 +185,11 @@ void EditorRender::renderSprite(
}
void EditorRender::renderCheckeredBackground(
doc::Image* image,
os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area)
{
m_renderer->renderCheckeredBackground(image, area);
m_renderer->renderCheckeredBackground(dstSurface, sprite, area);
}
void EditorRender::renderImage(

View File

@ -9,6 +9,7 @@
#define APP_UI_EDITOR_RENDER_H_INCLUDED
#pragma once
#include "app/render/renderer.h"
#include "doc/blend_mode.h"
#include "doc/color.h"
#include "doc/frame.h"
@ -35,7 +36,6 @@ namespace os {
namespace app {
class Doc;
class Renderer;
class EditorRender {
public:
@ -50,6 +50,10 @@ namespace app {
Type type() const;
void setType(const Type type);
const Renderer::Properties& properties() const {
return m_renderer->properties();
}
void setRefLayersVisiblity(const bool visible);
void setNonactiveLayersOpacity(const int opacity);
void setNewBlendMethod(const bool newBlend);
@ -81,17 +85,14 @@ namespace app {
void setOnionskin(const render::OnionskinOptions& options);
void disableOnionskin();
void renderSprite(
doc::Image* dstImage,
const doc::Sprite* sprite,
doc::frame_t frame);
void renderSprite(
os::Surface* dstSurface,
const doc::Sprite* sprite,
doc::frame_t frame,
const gfx::ClipF& area);
void renderCheckeredBackground(
doc::Image* image,
os::Surface* dstSurface,
const doc::Sprite* sprite,
const gfx::Clip& area);
void renderImage(
doc::Image* dst_image,

View File

@ -589,6 +589,11 @@ gfx::Size Graphics::doUIStringAlgorithm(const std::string& str, gfx::Color fg, g
return calculatedSize;
}
void Graphics::invalidate(const gfx::Rect& bounds)
{
dirty(gfx::Rect(bounds).offset(m_dx, m_dy));
}
void Graphics::dirty(const gfx::Rect& bounds)
{
gfx::Rect rc = m_surface->getClipBounds();

View File

@ -116,6 +116,9 @@ namespace ui {
static int measureUITextLength(const std::string& str, os::Font* font);
gfx::Size fitString(const std::string& str, int maxWidth, int align);
// Can be used in case you access/modify getInternalSurface() directly.
void invalidate(const gfx::Rect& bounds);
private:
gfx::Size doUIStringAlgorithm(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align, bool draw);
void dirty(const gfx::Rect& bounds);