diff options
author | Sebastian Geerken <devnull@localhost> | 2013-06-14 19:53:18 +0200 |
---|---|---|
committer | Sebastian Geerken <devnull@localhost> | 2013-06-14 19:53:18 +0200 |
commit | c72baf1e578ac14a3d861f8f6013557a5ba2c9dc (patch) | |
tree | 18aa2bab87ba0325dfa637e90304bf7a2242614b /dw | |
parent | 53920a06a5698c20fd519b672a362a9e268d8010 (diff) |
Better image scaling.
Diffstat (limited to 'dw')
-rw-r--r-- | dw/fltkimgbuf.cc | 95 | ||||
-rw-r--r-- | dw/fltkimgbuf.hh | 7 |
2 files changed, 102 insertions, 0 deletions
diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc index 3c3c9236..2feeecc2 100644 --- a/dw/fltkimgbuf.cc +++ b/dw/fltkimgbuf.cc @@ -119,6 +119,12 @@ void FltkImgbuf::setCMap (int *colors, int num_colors) inline void FltkImgbuf::scaleRow (int row, const core::byte *data) { + //scaleRowSimple (row, data); + scaleRowBeautiful (row, data); +} + +inline void FltkImgbuf::scaleRowSimple (int row, const core::byte *data) +{ int sr1 = scaledY (row); int sr2 = scaledY (row + 1); @@ -141,6 +147,84 @@ inline void FltkImgbuf::scaleRow (int row, const core::byte *data) } } +inline void FltkImgbuf::scaleRowBeautiful (int row, const core::byte *data) +{ + int sr1 = scaledY (row); + int sr2 = scaledY (row + 1); + + for (int sr = sr1; sr < sr2; sr++) + copiedRows->set (sr, true); + + if (height > root->height) + scaleBuffer (data, root->width, 1, + rawdata + sr1 * width * bpp, width, sr2 - sr1, + bpp); + else { + assert (sr1 ==sr2 || sr1 + 1 == sr2); + int row1 = backscaledY(sr1), row2 = backscaledY(sr1 + 1); + + // Draw only when all oginial lines are retrieved (speed). + bool allRootRows = true; + for (int r = row1; allRootRows && r < row2; r++) + allRootRows = allRootRows && root->copiedRows->get(r); + + if (allRootRows) + // We have to access root->rawdata (which has to be up to + // date), since a larger area than the single row is accessed + // here. + scaleBuffer (root->rawdata + row1 * root->width * bpp, + root->width, row2 - row1, + rawdata + sr1 * width * bpp, width, 1, + bpp); + } +} + +/** + * General method to scale an image buffer. Used to scale single lines + * in scaleRowBeautiful. + * + * The algorithm is rather simple. If the scaled buffer is smaller + * (both width and height) than the original surface, each pixel in + * the scaled surface is assigned a rectangle of pixels in the + * original surface; the resulting pixel value (red, green, blue) is + * simply the average of all pixel values. This is pretty fast and + * leads to rather good results. + * + * Nothing special (like interpolation) is done when scaling up. + * + * TODO Could be optimzized as in scaleRowSimple: when the destination + * image is larger, calculate only one row/column, and copy it to the + * other rows/columns. + */ +inline void FltkImgbuf::scaleBuffer (const core::byte *src, int srcWidth, + int srcHeight, core::byte *dest, + int destWidth, int destHeight, int bpp) +{ + for(int x = 0; x < destWidth; x++) + for(int y = 0; y < destHeight; y++) { + int xo1 = x * srcWidth / destWidth; + int xo2 = lout::misc::max ((x + 1) * srcWidth / destWidth, xo1 + 1); + int yo1 = y * srcHeight / destHeight; + int yo2 = lout::misc::max ((y + 1) * srcHeight / destHeight, yo1 + 1); + int n = (xo2 - xo1) * (yo2 - yo1); + + int v[bpp]; + for(int i = 0; i < bpp; i++) + v[i] = 0; + + for(int xo = xo1; xo < xo2; xo++) + for(int yo = yo1; yo < yo2; yo++) { + const core::byte *ps = src + bpp * (yo * srcWidth + xo); + for(int i = 0; i < bpp; i++) + v[i] += ps[i]; + } + + core::byte *pd = dest + bpp * (y * destWidth + x); + for(int i = 0; i < bpp; i++) + pd[i] = v[i] / n; + } +} + void FltkImgbuf::copyRow (int row, const core::byte *data) { assert (isRoot()); @@ -299,6 +383,17 @@ int FltkImgbuf::scaledY(int ySrc) return ySrc * height / root->height; } +int FltkImgbuf::backscaledY(int yScaled) +{ + assert (root != NULL); + + // Notice that rounding errors because of integers do not play a + // role. This method cannot be the exact iverse of scaledY, since + // skaleY is not bijective, and so not ivertible. Instead, both + // values always return the smallest value. + return yScaled * root->height / height; +} + void FltkImgbuf::draw (Fl_Widget *target, int xRoot, int yRoot, int x, int y, int width, int height) { diff --git a/dw/fltkimgbuf.hh b/dw/fltkimgbuf.hh index 34c6bfd8..d862eece 100644 --- a/dw/fltkimgbuf.hh +++ b/dw/fltkimgbuf.hh @@ -31,6 +31,7 @@ private: FltkImgbuf (Type type, int width, int height, FltkImgbuf *root); void init (Type type, int width, int height, FltkImgbuf *root); int scaledY(int ySrc); + int backscaledY(int yScaled); int isRoot() { return (root == NULL); } void detachScaledBuf (FltkImgbuf *scaledBuf); @@ -42,6 +43,12 @@ public: void setCMap (int *colors, int num_colors); inline void scaleRow (int row, const core::byte *data); + inline void scaleRowSimple (int row, const core::byte *data); + inline void scaleRowBeautiful (int row, const core::byte *data); + inline static void scaleBuffer (const core::byte *src, int srcWidth, + int srcHeight, core::byte *dest, + int destWidth, int destHeight, int bpp); + void newScan (); void copyRow (int row, const core::byte *data); core::Imgbuf* getScaledBuf (int width, int height); |