📄 imagerenderer.cpp
字号:
/* * Copyright (c) 2004 Nokia. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Nokia nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */#include <assert.h>#include <gdk/gdk.h>#include <gdk-pixbuf/gdk-pixbuf.h>#include "ImageRenderer.h"#include "CGContextProvider.h"#include "GdkXftContext.h"#include "GdkHelpers.h"extern "C"{static void size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer user_data);static void area_prepared(GdkPixbufLoader *loader, gpointer user_data);static void area_updated(GdkPixbufLoader *loader, gint arg1, gint arg2, gint arg3, gint arg4, gpointer user_data);static void closed(GdkPixbufLoader *loader,gpointer user_data);static gboolean anim_timeout(gpointer user_data);#if 0 // never usedstatic gboolean update_timeout(gpointer user_data);#endif}const int ImageRenderer::updateTimeoutMS = 100;/** ImageRenderer class for loading images and rendering images * * invariants: * pixbuf: valid pixbuf if * image should be regarded as still image * or image is still being loaded * 0 if image is animation and loaded * anim: animation data if * image is still being loaded * or image is valid animation * 0 if image is still image and loaded * * iter: valid animation iterator if image is animation and loaded * 0 image is still picture, object owns pixbuf * * loader: valid GdkPixbufLoader if stuff is being still loaded * 0 if data is not being loaded * * timeout_moment: valid only if animate_id != 0 * */ ImageRenderer::ImageRenderer() :refCnt(0) ,loader(0) ,anim(0) ,iter(0) ,pixbuf(0) ,pixmap(0) ,alpha(0) ,animate_id(0) ,cached(false) ,tileCached(false) ,offset(0){ fillGdkRectangle(&wantedSize, 0, 0, -1,-1);}ImageRenderer::ImageRenderer(const ImageRenderer& ir) :refCnt(0) ,loader(0) ,anim(0) ,iter(0) ,pixbuf(0) ,pixmap(0) ,alpha(0) ,animate_id(0) ,cached(false) ,tileCached(false) ,offset(0){ wantedSize = ir.wantedSize; if (ir.loader) { // still loading, treat as still picture but may have animation data // FIXME: Loader is not "copied", so what this means is that from copied ImageRenderers // where the loading is still on we will have only still images!! // we should only connect to signals but not "own" the loader. assert(!ir.iter); assert(ir.pixbuf); pixbuf = ir.pixbuf; g_object_ref(pixbuf); } else { // not loading if (ir.anim) { // is an animation => get own pixbuf, owned by iterator anim = ir.anim; g_object_ref(anim); assert(ir.iter); iter = gdk_pixbuf_animation_get_iter(anim, NULL); installAnimateTimeout(); } else { // not an animation assert(!ir.iter); pixbuf = ir.pixbuf; g_object_ref(pixbuf); } }}ImageRenderer::ImageRenderer(GdkPixbufAnimation* a) :refCnt(0) ,loader(0) ,anim(a) ,iter(0) ,pixbuf(0) ,pixmap(0) ,alpha(0) ,animate_id(0) ,cached(false) ,tileCached(false) ,offset(0){ assert(anim); fillGdkRectangle(&wantedSize, 0,0, -1,-1); // is an animation g_object_ref(anim); iter = gdk_pixbuf_animation_get_iter(anim, NULL); realSize(&wantedSize); installAnimateTimeout();}ImageRenderer::ImageRenderer(GdkPixbufLoader* ldr) :refCnt(0) ,loader(ldr) ,anim(0) ,iter(0) ,pixbuf(0) ,pixmap(0) ,alpha(0) ,animate_id(0) ,cached(false) ,tileCached(false) ,offset(0){ assert(ldr); fillGdkRectangle(&wantedSize,0,0,-1,-1); connectHandlers();}void ImageRenderer::connectHandlers(){ assert(loader); loader.connect("area-prepared", ::area_prepared, this); loader.connect("size-prepared", ::size_prepared, this); loader.connect("area-updated", ::area_updated, this); loader.connect("closed", ::closed, this);}ImageRenderer::~ImageRenderer(){ if (loader) { gdk_pixbuf_loader_close(loader, NULL); } removeAnimateTimeout(); invalidate(); if (iter) { g_object_unref(iter); g_object_unref(anim); } else { assert(!iter); assert(!anim); if (pixbuf) g_object_unref(pixbuf); }}bool ImageRenderer::incrementalLoadWithBytes(const void *bytes, unsigned length, bool isComplete){ if (!loader) { #if DEBUG g_printerr("called with null/closed loader");#endif assert(0); } GError* err = NULL; if ((length - offset) > 0) { bool succ = gdk_pixbuf_loader_write(loader, ((guchar*) bytes) + offset, length - offset, &err); if (succ) offset += length - offset; else { #if DEBUG g_printerr("error loading image incrementally. this:%x bytes %d, len:%d, isComplete: %d, offset:%d", (int) this, (int) bytes,length, (int) isComplete, offset);#endif } } if (isComplete) { err = NULL; gdk_pixbuf_loader_close(loader, &err); loader = 0; } return anim != NULL || pixbuf != NULL;}void ImageRenderer::realSize(GdkRectangle* rect){ if (iter) { fillGdkRectangle(rect, 0, 0, gdk_pixbuf_animation_get_width(anim), gdk_pixbuf_animation_get_height(anim)); } else { fillGdkRectangle(rect, 0, 0, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf)); }}void ImageRenderer::size(GdkRectangle* outSize){ assert(outSize); *outSize = wantedSize;}void ImageRenderer::resize(GdkRectangle* s){ assert(s); assert(s->x == 0); assert(s->y == 0); assert(s->width > 0); assert(s->height > 0); invalidate(); wantedSize = *s;}// Scales image or animation frame if resize() has been called.// Sets ImageRenderer::pixmap and ImageRenderer::alpha accordingly, so the image/frame can be renderedvoid ImageRenderer::cache(){ assert(!cached); GdkPixbuf* buf; cached = true; if (iter) buf = gdk_pixbuf_animation_iter_get_pixbuf(iter); else buf = pixbuf; GdkRectangle realsz; realSize(&realsz); if (wantedSize.width != realsz.width || wantedSize.height != realsz.height) { GdkPixbuf* scaledbuf = gdk_pixbuf_scale_simple(buf, wantedSize.width, wantedSize.height, GDK_INTERP_BILINEAR); gdk_pixbuf_render_pixmap_and_mask(scaledbuf, &pixmap, &alpha, 100); if (iter) { // animation -> discard scaled frame contents, only preserve the serverside representations g_object_unref(scaledbuf); } else { // still image -> scaled image becomes the representation of this image g_object_unref(pixbuf); pixbuf = scaledbuf; } return; } gdk_pixbuf_render_pixmap_and_mask(buf, &pixmap, &alpha, 100); assert(gdk_pixbuf_get_has_alpha(buf) == (alpha!=NULL));}void ImageRenderer::tileCache(){ if (!cached) cache(); // tilecache not implemented yet tileCached = true;}void ImageRenderer::invalidate(){ if (pixmap) { g_object_unref(pixmap); pixmap = 0; } if (alpha){ g_object_unref(alpha); alpha = 0; } cached = false; // invalidate tileCache tileCached = false;}void ImageRenderer::drawImageInRect(GdkRectangle* inRect, GdkRectangle* fromRect, NSCompositingOperation compositeOperator, CGContextRef context){ // scaling not supported
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -