diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp index e0af2671064b..0abebf99a87a 100644 --- a/graphics/yuv_to_rgb.cpp +++ b/graphics/yuv_to_rgb.cpp @@ -300,19 +300,8 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS convertYUV420ToRGB((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } -#define OUTPUT_4_PIXEL_COLUMN() \ - PUT_PIXEL(*ySrc, dstPtr); \ - PUT_PIXEL(*(ySrc + yPitch), dstPtr + dstPitch); \ - PUT_PIXEL(*(ySrc + (yPitch << 1)), dstPtr + dstPitch * 2); \ - PUT_PIXEL(*(ySrc + (yPitch * 3)), dstPtr + dstPitch * 3); \ - ySrc++; \ - dstPtr += sizeof(PixelInt) - template void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { - int quarterHeight = yHeight >> 2; - int quarterWidth = yWidth >> 2; - // Keep the tables in pointers here to avoid a dereference on each pixel const int16 *Cr_r_tab = lookup->_colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; @@ -320,26 +309,44 @@ void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup const int16 *Cb_b_tab = Cb_g_tab + 256; const uint32 *rgbToPix = lookup->_rgbToPix; - for (int h = 0; h < quarterHeight; h++) { - for (int w = 0; w < quarterWidth; w++) { - register const uint32 *L; - - int16 cr_r = Cr_r_tab[*vSrc]; - int16 crb_g = Cr_g_tab[*vSrc] + Cb_g_tab[*uSrc]; - int16 cb_b = Cb_b_tab[*uSrc]; - ++uSrc; - ++vSrc; - - OUTPUT_4_PIXEL_COLUMN(); - OUTPUT_4_PIXEL_COLUMN(); - OUTPUT_4_PIXEL_COLUMN(); - OUTPUT_4_PIXEL_COLUMN(); + for (int y = 0; y < yHeight; y++) { + for (int x = 0; x < yWidth; x++) { + // Perform bilinear interpolation on the the chroma values + // Based on the algorithm found here: http://tech-algorithm.com/articles/bilinear-image-scaling/ + // Feel free to optimize further + int targetX = x >> 2; + int targetY = y >> 2; + int xDiff = x & 3; + int yDiff = y & 3; + int index = targetY * uvPitch + targetX; + + byte a = uSrc[index]; + byte b = uSrc[index + 1]; + byte c = uSrc[index + uvPitch]; + byte d = uSrc[index + uvPitch + 1]; + + byte u = (a * (4 - xDiff) * (4 - yDiff) + b * xDiff * (4 - yDiff) + + c * yDiff * (4 - xDiff) + d * xDiff * yDiff) >> 4; + + a = vSrc[index]; + b = vSrc[index + 1]; + c = vSrc[index + uvPitch]; + d = vSrc[index + uvPitch + 1]; + + byte v = (a * (4 - xDiff) * (4 - yDiff) + b * xDiff * (4 - yDiff) + + c * yDiff * (4 - xDiff) + d * xDiff * yDiff) >> 4; + + int16 cr_r = Cr_r_tab[v]; + int16 crb_g = Cr_g_tab[v] + Cb_g_tab[u]; + int16 cb_b = Cb_b_tab[u]; + const uint32 *L; + + PUT_PIXEL(ySrc[x], dstPtr); + dstPtr += sizeof(PixelInt); } - dstPtr += dstPitch * 3; - ySrc += (yPitch << 2) - yWidth; - uSrc += uvPitch - quarterWidth; - vSrc += uvPitch - quarterWidth; + dstPtr += dstPitch - yWidth * sizeof(PixelInt); + ySrc += yPitch; } } diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h index 52ab2ebdfbe7..73a2c69d7d43 100644 --- a/graphics/yuv_to_rgb.h +++ b/graphics/yuv_to_rgb.h @@ -67,6 +67,11 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS /** * Convert a YUV410 image to an RGB surface * + * Since the chroma has a very low resolution in 410, we perform bilinear scaling + * on the two chroma planes to produce the image. The chroma planes must have + * at least one extra row that can be read from in order to produce a proper + * image (filled with 0x80). This is required in order to speed up this function. + * * @param dst the destination surface * @param ySrc the source of the y component * @param uSrc the source of the u component diff --git a/video/codecs/svq1.cpp b/video/codecs/svq1.cpp index dc64429d4a21..eba0c90305d5 100644 --- a/video/codecs/svq1.cpp +++ b/video/codecs/svq1.cpp @@ -191,14 +191,18 @@ const Graphics::Surface *SVQ1Decoder::decodeImage(Common::SeekableReadStream *st for (int i = 0; i < 3; i++) { int width, height; if (i == 0) { - width = yWidth; + width = yWidth; height = yHeight; + current[i] = new byte[width * height]; } else { - width = uvWidth; + width = uvWidth; height = uvHeight; - } - current[i] = new byte[width * height]; + // Add an extra row's worth of data to not go out-of-bounds in the + // color conversion. Then fill that with "empty" data. + current[i] = new byte[width * (height + 1)]; + memset(current[i] + width * height, 0x80, width); + } if (frameType == 0) { // I Frame // Keyframe (I)