aboutsummaryrefslogtreecommitdiff
path: root/src/layer_image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/layer_image.cpp')
-rw-r--r--src/layer_image.cpp170
1 files changed, 170 insertions, 0 deletions
diff --git a/src/layer_image.cpp b/src/layer_image.cpp
new file mode 100644
index 0000000..a0d6d55
--- /dev/null
+++ b/src/layer_image.cpp
@@ -0,0 +1,170 @@
+#include <math.h>
+
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_rotozoom.h>
+
+/* HACK: older SDL_gfx versions define GFX_ALPHA_ADJUST in the header */
+#define GFX_ALPHA_ADJUST \
+ static __attribute__((unused)) GFX_ALPHA_ADJUST
+#include <SDL_gfxBlitFunc.h>
+#undef GFX_ALPHA_ADJUST
+
+#include "osc_graphics.h"
+#include "layer_image.h"
+
+Layer::CtorInfo LayerImage::ctor_info = {"image", "s" /* file */};
+
+static inline void
+rgba_blit_with_alpha(SDL_Surface *src_surf, SDL_Surface *dst_surf, Uint8 alpha)
+{
+ Uint8 *src = (Uint8 *)src_surf->pixels;
+ Uint8 *dst = (Uint8 *)dst_surf->pixels;
+ SDL_PixelFormat *fmt = src_surf->format;
+
+ int inc = fmt->BytesPerPixel;
+ int len = src_surf->w * src_surf->h;
+
+ SDL_MAYBE_LOCK(src_surf);
+ SDL_MAYBE_LOCK(dst_surf);
+
+ GFX_DUFFS_LOOP4({
+ register Uint32 pixel;
+ register int a;
+
+ pixel = *(Uint32 *)src;
+ a = ((pixel & fmt->Amask) >> fmt->Ashift) << fmt->Aloss;
+ a = (a*alpha)/SDL_ALPHA_OPAQUE;
+ a = (a << fmt->Aloss) << fmt->Ashift;
+ pixel &= ~fmt->Amask;
+ pixel |= a;
+ *(Uint32 *)dst = pixel;
+
+ src += inc;
+ dst += inc;
+ }, len)
+
+ SDL_MAYBE_UNLOCK(dst_surf);
+ SDL_MAYBE_UNLOCK(src_surf);
+}
+
+LayerImage::LayerImage(const char *name, SDL_Rect geo, float opacity,
+ const char *file) :
+ Layer(name),
+ surf_alpha(NULL), surf_scaled(NULL), surf(NULL)
+{
+ file_osc_id = register_method("file", "s",
+ (OSCServer::MethodHandlerCb)file_osc);
+
+ LayerImage::alpha(opacity);
+ LayerImage::geo(geo);
+ LayerImage::file(file);
+}
+
+void
+LayerImage::geo(SDL_Rect geo)
+{
+ if (!geo.x && !geo.y && !geo.w && !geo.h)
+ geov = (SDL_Rect){0, 0, screen->w, screen->h};
+ else
+ geov = geo;
+
+ if (!surf)
+ return;
+
+ if (surf_scaled &&
+ surf_scaled->w == geov.w && surf_scaled->h == geov.h)
+ return;
+
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+
+ if (surf->w != geov.w || surf->h != geov.h) {
+ surf_scaled = zoomSurface(surf,
+ (double)geov.w/surf->w,
+ (double)geov.h/surf->h,
+ SMOOTHING_ON);
+ }
+
+ alpha(alphav);
+}
+
+void
+LayerImage::alpha(float opacity)
+{
+ SDL_Surface *use_surf = surf_scaled ? : surf;
+ Uint8 alpha = (Uint8)ceilf(opacity*SDL_ALPHA_OPAQUE);
+
+ alphav = opacity;
+
+ if (!use_surf)
+ return;
+
+ if (!use_surf->format->Amask) {
+ if (alpha == SDL_ALPHA_OPAQUE)
+ SDL_SetAlpha(use_surf, 0, 0);
+ else
+ SDL_SetAlpha(use_surf, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+
+ return;
+ }
+
+ if (alpha == SDL_ALPHA_OPAQUE) {
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ return;
+ }
+
+ if (!surf_alpha) {
+ surf_alpha = SDL_CreateRGBSurface(use_surf->flags,
+ use_surf->w, use_surf->h,
+ use_surf->format->BitsPerPixel,
+ use_surf->format->Rmask,
+ use_surf->format->Gmask,
+ use_surf->format->Bmask,
+ use_surf->format->Amask);
+ }
+
+ if (alpha == SDL_ALPHA_TRANSPARENT) {
+ SDL_FillRect(surf_alpha, NULL,
+ SDL_MapRGBA(surf_alpha->format,
+ 0, 0, 0, SDL_ALPHA_TRANSPARENT));
+ } else {
+ rgba_blit_with_alpha(use_surf, surf_alpha, alpha);
+ }
+}
+
+void
+LayerImage::file(const char *file)
+{
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+ SDL_FREESURFACE_SAFE(surf);
+
+ if (!file || !*file)
+ return;
+
+ surf = IMG_Load(file);
+ if (!surf) {
+ SDL_IMAGE_ERROR("IMG_Load");
+ exit(EXIT_FAILURE);
+ }
+
+ geo(geov);
+}
+
+void
+LayerImage::frame(SDL_Surface *target)
+{
+ if (surf)
+ SDL_BlitSurface(surf_alpha ? : surf_scaled ? : surf, NULL,
+ target, &geov);
+}
+
+LayerImage::~LayerImage()
+{
+ unregister_method(file_osc_id);
+
+ SDL_FREESURFACE_SAFE(surf_alpha);
+ SDL_FREESURFACE_SAFE(surf_scaled);
+ SDL_FREESURFACE_SAFE(surf);
+}