/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ------------------------------------------------------------------------------*/ #include #include "picture/pict.h" #include "color/color.h" #include "log/log.h" using namespace GS; //------------------------------------------------------------------------------ bool Picture::Reframe(int offset_sx, int offset_sy, int offset_ex, int offset_ey, const Color *fill) { if (!GetData() || (GetPixelFormat().GetBpp() != 32)) return false; int _width = GetWidth() - offset_sx + offset_ex, _height = GetHeight() - offset_sy + offset_ey; uint *new_data = (uint *)AllocMemory(sizeof(uint) * _width * _height), *_d = new_data; if (!new_data) __ERR__(__LOG_E__ << "Failed to allocate destination buffer.\n", false) // Fill color. uint _fill = fill ? GetPixelFormat().Format(fill->x, fill->y, fill->z, fill->w) : GetPixelFormat().Format(0, 0, 0); // Top framing. for (int y = offset_sy; y < 0; ++y) for (int x = 0; x < _width; ++x) *_d++ = _fill; // Blit + left/right framing. int blit_ex = offset_ex > 0 ? GetWidth() : GetWidth() + offset_ex, blit_ey = offset_ey > 0 ? GetHeight() : GetHeight() + offset_ey; uint *s = (uint *)GetData(); if (offset_sy > 0) s += GetWidth() * offset_sy; for (int y = offset_sy > 0 ? offset_sy : 0; y < blit_ey; ++y) { uint *_s = s; for (int x = offset_sx; x < 0; ++x) *_d++ = _fill; // Left framing for (int x = offset_sx > 0 ? offset_sx : 0; x < blit_ex; ++x) *_d++ = _s[x]; // Blit for (int x = 0; x < offset_ex; ++x) *_d++ = _fill; // Right framing s += GetWidth(); } // Bottom framing. for (int y = 0; y < offset_ey; ++y) for (int x = 0; x < _width; ++x) *_d++ = _fill; SetData(new_data, _width, _height, GetPixelFormat().GetDesc(), true); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Picture::Flip(bool flip_h, bool flip_v) { if (!GetData() || (GetPixelFormat().GetBpp() != 32)) return false; uint *s = (uint *)GetData(), *d = s, t; if (flip_h) { if (flip_v) { d += GetWidth() * GetHeight() - 1; while (d > s) { t = *s; *s++ = *d; *d-- = t; } } else { for (uint n = 0; n < GetHeight(); ++n) { uint *_s = s, *_d = d + GetWidth() - 1; while (_d > _s) { t = *_s; *_s++ = *_d; *_d-- = t; } s += GetWidth(); d += GetWidth(); } } } else if (flip_v) { d += GetWidth() * (GetHeight() - 1); while (d > s) { for (uint n = 0; n < GetWidth(); ++n) { t = s[n]; s[n] = d[n]; d[n] = t; } s += GetWidth(); d -= GetWidth(); } } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static void WindowClip(const Rect *a, Rect *fa, const Rect *b, Rect *fb) { Rect _b(*b); _b.ex = _b.sx + a->GetWidth(); _b.ey = _b.sy + a->GetHeight(); // Clip source rectangle and correct destination rectangle. *fa = fa->Intersection(*a); _b.sx += fa->sx - a->sx; _b.sy += fa->sy - a->sy; _b.ex += fa->ex - a->ex; _b.ey += fa->ey - a->ey; // Clip destination rectangle and correct source rectangle. *fb = fb->Intersection(_b); fa->sx += fb->sx - _b.sx; fa->sy += fb->sy - _b.sy; fa->ex += fb->ex - _b.ex; fa->ey += fb->ey - _b.ey; } static void WindowStretch(Rect *a, Rect *fa, Rect *b, Rect *fb) { float ku = (float)b->GetWidth() / (float)a->GetWidth(), kv = (float)b->GetHeight() / (float)a->GetHeight(); // Clip source rectangle and correct destination rectangle. fa[0] = fa->Intersection(a[0]); b->sx += (fa->sx - a->sx) * ku; b->sy += (fa->sy - a->sy) * kv; b->ex += (fa->ex - a->ex) * ku; b->ey += (fa->ey - a->ey) * kv; // Clip destination rectangle and correct source rectangle. fb[0] = fb->Intersection(b[0]); ku = 1 / ku; kv = 1 / kv; fa->sx += (fb->sx - b->sx) * ku; fa->sy += (fb->sy - b->sy) * kv; fa->ex += (fb->ex - b->ex) * ku; fa->ey += (fb->ey - b->ey) * kv; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Picture::BlitMask(const Picture &src, Picture &dst, Picture &msk, const Rect *src_rect, const Rect *dst_rect) { if (!src.GetData() || !dst.GetData()) return false; // Default blit rectangles. Rect default_a(src.GetRect()), default_b(dst.GetRect()); if (!src_rect) src_rect = &default_a; if (!dst_rect) dst_rect = &default_b; // Compute blit window. Rect frame_a(src.GetRect()), frame_b(dst.GetRect()); WindowClip(src_rect, &frame_a, dst_rect, &frame_b); // Limit to mask dimensions. if (frame_b.GetWidth() > (int)msk.GetWidth()) frame_b.SetWidth(msk.GetWidth()); if (frame_b.GetHeight() > (int)msk.GetHeight()) frame_b.SetHeight(msk.GetHeight()); if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0)) return false; PixelFormatDescription initial = dst.GetPixelFormat().GetDesc(); dst.Convert(src.GetPixelFormat().GetDesc()); // Perform blit. uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy), *pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy), *pmsk = msk.GetData(); for (int n = 0; n < frame_b.GetHeight(); n++) { uchar *_pdst = pdst, *_psrc = psrc, *_pmsk = pmsk; for (int x = frame_b.GetWidth(); x--; ) { uchar alpha = _pmsk[3]; uchar a_blend = AlphaCompositeAlpha(_pdst[3], alpha); _pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], alpha, a_blend); _pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], alpha, a_blend); _pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], alpha, a_blend); _pdst[3] = a_blend; _psrc += 4; _pdst += 4; _pmsk += 4; } psrc += src.GetPitch(); pdst += dst.GetPitch(); pmsk += msk.GetPitch(); } dst.Convert(initial); return true; } bool Picture::Blit(const Picture &src, Picture &dst, const Rect *src_rect, const Rect *dst_rect, BlendMode mode) { if (!src.GetData() || !dst.GetData()) return false; // Default blit rectangles. Rect default_a(src.GetRect()), default_b(dst.GetRect()); if (!src_rect) src_rect = &default_a; if (!dst_rect) dst_rect = &default_b; // Compute blit window. Rect frame_a(src.GetRect()), frame_b(dst.GetRect()); WindowClip(src_rect, &frame_a, dst_rect, &frame_b); if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0)) return false; PixelFormatDescription initial = dst.GetPixelFormat().GetDesc(); dst.Convert(src.GetPixelFormat().GetDesc()); // Perform blit. uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy), *pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy); for (int n = 0; n < frame_b.GetHeight(); n++) { uchar *_pdst = pdst, *_psrc = psrc; switch (mode) { case RgbToAlpha: for (int x = frame_b.GetWidth(); x--; ) { _pdst[3] = (_psrc[0] + _psrc[1] + _psrc[2]) / 3; _psrc += 4; _pdst += 4; } break; case BlendReplace: memmove(pdst, psrc, frame_b.GetWidth() * dst.GetBpp() / 8); break; case BlendComposeFast: for (int x = frame_b.GetWidth(); x--; ) { int a = _psrc[3], ia = 255 - a; _pdst[0] = uchar((_psrc[0] * a + _pdst[0] * ia) >> 8); _pdst[1] = uchar((_psrc[1] * a + _pdst[1] * ia) >> 8); _pdst[2] = uchar((_psrc[2] * a + _pdst[2] * ia) >> 8); _pdst[3] = uchar(Types::Max (_pdst[3], a)); _psrc += 4; _pdst += 4; } break; case BlendCompose: for (int x = frame_b.GetWidth(); x--; ) { uchar a_blend = AlphaCompositeAlpha(_pdst[3], _psrc[3]); _pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], _psrc[3], a_blend); _pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], _psrc[3], a_blend); _pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], _psrc[3], a_blend); _pdst[3] = a_blend; _psrc += 4; _pdst += 4; } break; default: break; } psrc += src.GetPitch(); pdst += dst.GetPitch(); } dst.Convert(initial); return true; } bool Picture::ScaleBlit(Picture &src, Picture &dst, Rect *src_rect, Rect *dst_rect) { if (!src.GetData() || !dst.GetData()) return false; // Default blitting rectangles. Rect default_a(src.GetRect().AsFloat()), default_b(dst.GetRect().AsFloat()); if (!src_rect) src_rect = &default_a; if (!dst_rect) dst_rect = &default_b; Rect frame_a(src.GetRect().AsFloat()), frame_b(dst.GetRect().AsFloat()); WindowStretch(src_rect, &frame_a, dst_rect, &frame_b); if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0)) return false; // Apply sub-pixel/sub-texel correction. float ku = frame_a.GetWidth() / frame_b.GetWidth(), kv = frame_a.GetHeight() / frame_b.GetHeight(); float fb_isx = Math::Floor(frame_b.sx), fb_isy = Math::Floor(frame_b.sy), fb_iex = Math::Floor(frame_b.ex), fb_iey = Math::Floor(frame_b.ey); float dt_su = (frame_b.sx - fb_isx), dt_sv = (frame_b.sy - fb_isy), dt_eu = (frame_b.ex - fb_iex), dt_ev = (frame_b.ey - fb_iey); frame_a.sx -= dt_su * ku; frame_a.sy -= dt_sv * kv; frame_a.ex -= dt_eu * ku; frame_a.ey -= dt_ev * kv; frame_b.sx = fb_isx; frame_b.sy = fb_isy; frame_b.ex = fb_iex; frame_b.ey = fb_iey; // Convert sub pixel deltas to blending coefficients. dt_su = 1 - dt_su; dt_sv = 1 - dt_sv; uint *pdst = (uint *)dst.GetDataOffset((uint)frame_b.sx, (uint)frame_b.sy); int src_width = (int)frame_b.GetWidth(), src_height = (int)frame_b.GetHeight(); float v = frame_a.sy; for (int y = 0; y < src_height; y++) { float u = frame_a.sx; // Blended or opaque scanline. if ((!y) || (y == (src_height - 1))) { float kfrst, kscan, klast; // Set blend coefficients. if (!y) { kfrst = dt_su * dt_sv; // top-left kscan = dt_sv; // top klast = dt_eu * dt_sv; // top-right } else { kfrst = dt_su * dt_ev; // bottom-left kscan = dt_ev; // bottom klast = dt_eu * dt_ev; // bottom-right } // 1st pixel, scanline, last pixel. pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), kfrst); u += ku; int x; for (x = 1; x < (src_width - 1); x++) { pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), kscan); u += ku; } pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), klast); } else { pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), dt_su); u += ku; int x; for (x = 1; x < (src_width - 1); x++) { pdst[x] = src.SampleInteger(u, v); u += ku; } pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), dt_eu); } pdst += dst.GetWidth(); v += kv; } return true; } //------------------------------------------------------------------------------