1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
6
7#include <gdk/gdk.h>
8
9#include "base/basictypes.h"
10#include "base/logging.h"
11#include "third_party/skia/include/core/SkBitmap.h"
12#include "third_party/skia/include/core/SkUnPreMultiply.h"
13
14namespace libgtk2ui {
15
16const int kSkiaToGDKMultiplier = 257;
17
18// GDK_COLOR_RGB multiplies by 257 (= 0x10001) to distribute the bits evenly
19// See: http://www.mindcontrol.org/~hplus/graphics/expand-bits.html
20// To get back, we can just right shift by eight
21// (or, formulated differently, i == (i*257)/256 for all i < 256).
22
23SkColor GdkColorToSkColor(GdkColor color) {
24  return SkColorSetRGB(color.red >> 8, color.green >> 8, color.blue >> 8);
25}
26
27GdkColor SkColorToGdkColor(SkColor color) {
28  GdkColor gdk_color = {
29      0,
30      static_cast<guint16>(SkColorGetR(color) * kSkiaToGDKMultiplier),
31      static_cast<guint16>(SkColorGetG(color) * kSkiaToGDKMultiplier),
32      static_cast<guint16>(SkColorGetB(color) * kSkiaToGDKMultiplier)
33  };
34  return gdk_color;
35}
36
37const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf) {
38  // TODO(erg): What do we do in the case where the pixbuf fails these dchecks?
39  // I would prefer to use our gtk based canvas, but that would require
40  // recompiling half of our skia extensions with gtk support, which we can't
41  // do in this build.
42  DCHECK_EQ(GDK_COLORSPACE_RGB, gdk_pixbuf_get_colorspace(pixbuf));
43
44  int n_channels = gdk_pixbuf_get_n_channels(pixbuf);
45  int w = gdk_pixbuf_get_width(pixbuf);
46  int h = gdk_pixbuf_get_height(pixbuf);
47
48  SkBitmap ret;
49  ret.setConfig(SkBitmap::kARGB_8888_Config, w, h);
50  ret.allocPixels();
51  ret.eraseColor(0);
52
53  uint32_t* skia_data = static_cast<uint32_t*>(ret.getAddr(0, 0));
54
55  if (n_channels == 4) {
56    int total_length = w * h;
57    guchar* gdk_pixels = gdk_pixbuf_get_pixels(pixbuf);
58
59    // Now here's the trick: we need to convert the gdk data (which is RGBA and
60    // isn't premultiplied) to skia (which can be anything and premultiplied).
61    for (int i = 0; i < total_length; ++i, gdk_pixels += 4) {
62      const unsigned char& red = gdk_pixels[0];
63      const unsigned char& green = gdk_pixels[1];
64      const unsigned char& blue = gdk_pixels[2];
65      const unsigned char& alpha = gdk_pixels[3];
66
67      skia_data[i] = SkPreMultiplyARGB(alpha, red, green, blue);
68    }
69  } else if (n_channels == 3) {
70    // Because GDK makes rowstrides word aligned, we need to do something a bit
71    // more complex when a pixel isn't perfectly a word of memory.
72    int rowstride = gdk_pixbuf_get_rowstride(pixbuf);
73    guchar* gdk_pixels = gdk_pixbuf_get_pixels(pixbuf);
74    for (int y = 0; y < h; ++y) {
75      int row = y * rowstride;
76
77      for (int x = 0; x < w; ++x) {
78        guchar* pixel = gdk_pixels + row + (x * 3);
79        const unsigned char& red = pixel[0];
80        const unsigned char& green = pixel[1];
81        const unsigned char& blue = pixel[2];
82
83        skia_data[y * w + x] = SkPreMultiplyARGB(255, red, green, blue);
84      }
85    }
86  } else {
87    NOTREACHED();
88  }
89
90  return ret;
91}
92
93GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
94  if (bitmap.isNull())
95    return NULL;
96
97  SkAutoLockPixels lock_pixels(bitmap);
98
99  int width = bitmap.width();
100  int height = bitmap.height();
101
102  GdkPixbuf* pixbuf =
103      gdk_pixbuf_new(GDK_COLORSPACE_RGB,  // The only colorspace gtk supports.
104                     TRUE,                // There is an alpha channel.
105                     8,
106                     width,
107                     height);
108
109  // SkBitmaps are premultiplied, we need to unpremultiply them.
110  const int kBytesPerPixel = 4;
111  uint8* divided = gdk_pixbuf_get_pixels(pixbuf);
112
113  for (int y = 0, i = 0; y < height; y++) {
114    for (int x = 0; x < width; x++) {
115      uint32 pixel = bitmap.getAddr32(0, y)[x];
116
117      int alpha = SkColorGetA(pixel);
118      if (alpha != 0 && alpha != 255) {
119        SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel);
120        divided[i + 0] = SkColorGetR(unmultiplied);
121        divided[i + 1] = SkColorGetG(unmultiplied);
122        divided[i + 2] = SkColorGetB(unmultiplied);
123        divided[i + 3] = alpha;
124      } else {
125        divided[i + 0] = SkColorGetR(pixel);
126        divided[i + 1] = SkColorGetG(pixel);
127        divided[i + 2] = SkColorGetB(pixel);
128        divided[i + 3] = alpha;
129      }
130      i += kBytesPerPixel;
131    }
132  }
133
134  return pixbuf;
135}
136
137}  // namespace libgtk2ui
138