kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b
kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b
kARGB32_Format, //!< SkPMColor
+ kLCD16_Format //!< 565 alpha for r/g/b
};
enum {
return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
}
+ /**
+ * Return the address of the specified 16bit mask. In the debug build,
+ * this asserts that the mask's format is kLCD16_Format, and that (x,y)
+ * are contained in the mask's fBounds.
+ */
+ uint16_t* getAddrLCD16(int x, int y) const {
+ SkASSERT(kLCD16_Format == fFormat);
+ SkASSERT(fBounds.contains(x, y));
+ SkASSERT(fImage != NULL);
+ uint16_t* row = (uint16_t*)(fImage + (y - fBounds.fTop) * fRowBytes);
+ return row + (x - fBounds.fLeft);
+ }
+
/** Return an address into the 32-bit plane of an LCD or VerticalLCD mask
for the given position.
*/
static uint8_t* AllocImage(size_t bytes);
static void FreeImage(void* image);
-
+
enum CreateMode {
kJustComputeBounds_CreateMode, //!< compute bounds and return
kJustRenderImage_CreateMode, //!< render into preallocate mask
fMaskFormat = MASK_FORMAT_UNKNOWN;
}
- unsigned rowBytes() const {
- unsigned rb = fWidth;
- if (SkMask::kBW_Format == fMaskFormat) {
+ /**
+ * Compute the rowbytes for the specified width and mask-format.
+ */
+ static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) {
+ unsigned rb = width;
+ if (SkMask::kBW_Format == format) {
rb = (rb + 7) >> 3;
- } else if (SkMask::kARGB32_Format == fMaskFormat) {
+ } else if (SkMask::kARGB32_Format == format) {
rb <<= 2;
+ } else if (SkMask::kLCD16_Format == format) {
+ rb = SkAlign4(rb << 1);
} else {
rb = SkAlign4(rb);
}
return rb;
}
+ unsigned rowBytes() const {
+ return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat);
+ }
+
bool isJustAdvance() const {
return MASK_FORMAT_JUST_ADVANCE == fMaskFormat;
}
#include "SkColorPriv.h"
#include "SkImageDecoder.h"
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+ SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+ paint->setTypeface(face);
+ SkSafeUnref(face);
+}
+
#if 0
static int newscale(U8CPU a, U8CPU b, int shift) {
unsigned prod = a * b + (1 << (shift - 1));
bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL);
bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL);
bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL);
-
+
int rgb = 0x18;
int r = rgb >> 3;
int g = rgb >> 2;
SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb);
uint16_t newdst = SkSrcOver32To16(pm, dst);
sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width());
-
+
int ia = 255 - alpha;
int iscale = SkAlpha255To256(ia);
int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3;
int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3;
int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2;
-
+
sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width());
// if (mr != dr || mg != dg)
// SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2);
}
}
-
+
SkScalar dx = SkIntToScalar(width+4);
-
+
canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0);
canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0);
canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0);
-
+
SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) };
SkPaint p;
p.setARGB(0xFF, rgb, rgb, rgb);
}
#endif
-static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
+static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
src->allocPixels();
src->eraseColor(0);
SkScalar hh = SkIntToScalar(h);
p.setAntiAlias(true);
- p.setColor(0xFFFFCC44);
+ p.setColor(0xFFFFCC44);
r.set(0, 0, ww*3/4, hh*3/4);
c.drawOval(r, p);
-
+
dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
dst->allocPixels();
dst->eraseColor(0);
SkScalar x, SkScalar y) {
SkPaint p;
- canvas->drawBitmap(fSrcB, x, y, &p);
+ canvas->drawBitmap(fSrcB, x, y, &p);
p.setAlpha(alpha);
p.setXfermode(mode);
canvas->drawBitmap(fDstB, x, y, &p);
}
-
+
public:
const static int W = 64;
const static int H = 64;
XfermodesView() {
const int W = 64;
const int H = 64;
-
+
fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
fBG.setPixels(gBG);
fBG.setIsOpaque(true);
-
+
make_bitmaps(W, H, &fSrcB, &fDstB);
}
-
+
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
void drawBG(SkCanvas* canvas) {
canvas->drawColor(SK_ColorWHITE);
}
-
+
virtual void onDraw(SkCanvas* canvas) {
canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
this->drawBG(canvas);
-
+
const struct {
SkXfermode::Mode fMode;
const char* fLabel;
SkMatrix m;
m.setScale(SkIntToScalar(6), SkIntToScalar(6));
s->setLocalMatrix(m);
-
+
SkPaint labelP;
labelP.setAntiAlias(true);
+ labelP.setLCDRenderText(true);
labelP.setTextAlign(SkPaint::kCenter_Align);
-
+ setNamedTypeface(&labelP, "Menlo Regular");
+// labelP.setTextSize(SkIntToScalar(11));
+
const int W = 5;
SkScalar x0 = 0;
p.setStyle(SkPaint::kFill_Style);
p.setShader(s);
canvas->drawRect(r, p);
-
+
canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
// canvas->save();
draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
using namespace skia_blitter_support;
#endif
+///////////////////////////////////////////////////////////////////////////////
+
+static int upscale31To256(int value) {
+ SkASSERT((unsigned)value <= 31);
+ // 0..31 -> 0..255
+ value = (value << 3) | (value >> 2);
+ // 0..255 -> 0..256
+ value += (value >> 7);
+ SkASSERT((unsigned)value <= 256);
+ return value;
+}
+
+static void blit_lcd16_opaque(SkPMColor dst[], const uint16_t src[],
+ SkPMColor color, int width) {
+ int srcR = SkGetPackedR32(color);
+ int srcG = SkGetPackedG32(color);
+ int srcB = SkGetPackedB32(color);
+
+ for (int i = 0; i < width; i++) {
+ uint16_t mask = src[i];
+ if (0 == mask) {
+ continue;
+ }
+
+ /* We want all of these in 5bits, hence the shifts in case one of them
+ * (green) is 6bits.
+ */
+ int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5);
+ int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5);
+ int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5);
+
+ // Now upscale them to 0..256, so we can use SkAlphaBlend
+ maskR = upscale31To256(maskR);
+ maskG = upscale31To256(maskG);
+ maskB = upscale31To256(maskB);
+
+ int maskA = SkMax32(SkMax32(maskR, maskG), maskB);
+
+ SkPMColor d = dst[i];
+ int dstA = SkGetPackedA32(d);
+ int dstR = SkGetPackedR32(d);
+ int dstG = SkGetPackedG32(d);
+ int dstB = SkGetPackedB32(d);
+
+ dst[i] = SkPackARGB32(SkAlphaBlend(0xFF, dstA, maskA),
+ SkAlphaBlend(srcR, dstR, maskR),
+ SkAlphaBlend(srcG, dstG, maskG),
+ SkAlphaBlend(srcB, dstB, maskB));
+ }
+}
+
+static void blitmask_lcd16(const SkBitmap& device, const SkMask& mask,
+ const SkIRect& clip, SkPMColor srcColor) {
+ int x = clip.fLeft;
+ int y = clip.fTop;
+ int width = clip.width();
+ int height = clip.height();
+
+ SkPMColor* dstRow = device.getAddr32(x, y);
+ const uint16_t* srcRow = mask.getAddrLCD16(x, y);
+
+ do {
+ blit_lcd16_opaque(dstRow, srcRow, srcColor, width);
+ dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes());
+ srcRow = (const uint16_t*)((const char*)srcRow + mask.fRowBytes);
+ } while (--height != 0);
+}
+
//////////////////////////////////////////////////////////////////////////////////////
static void SkARGB32_Blit32(const SkBitmap& device, const SkMask& mask,
} else if (SkMask::kARGB32_Format == mask.fFormat) {
SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
return;
+ } else if (SkMask::kLCD16_Format == mask.fFormat) {
+ blitmask_lcd16(fDevice, mask, clip, fPMColor);
+ return;
}
int x = clip.fLeft;
} else if (SkMask::kARGB32_Format == mask.fFormat) {
SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
return;
+ } else if (SkMask::kLCD16_Format == mask.fFormat) {
+ blitmask_lcd16(fDevice, mask, clip, fPMColor);
+ return;
}
int x = clip.fLeft;
SkARGB32_BlitBW(fDevice, mask, clip, black);
} else if (SkMask::kARGB32_Format == mask.fFormat) {
SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
+ } else if (SkMask::kLCD16_Format == mask.fFormat) {
+ blitmask_lcd16(fDevice, mask, clip, fPMColor);
} else {
#if defined(SK_SUPPORT_LCDTEXT)
const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
}
+static bool canSupportLCD16(const SkPaint& paint) {
+#if 0
+ return !paint.getShader() &&
+ !paint.getXfermode() && // unless its srcover
+ !paint.getMaskFilter() &&
+ !paint.getRasterizer() &&
+ !paint.getColorFilter() &&
+ !paint.getPathEffect() &&
+ !paint.isFakeBoldText() &&
+ paint.getStyle() == SkPaint::kFill_Style;
+#else
+ // disable for now, while we test more
+ return false;
+#endif
+}
+
static SkMask::Format computeMaskFormat(const SkPaint& paint) {
uint32_t flags = paint.getFlags();
if (!(flags & SkPaint::kAntiAlias_Flag))
return SkMask::kBW_Format;
+ if (flags & SkPaint::kLCDRenderText_Flag) {
+ if (canSupportLCD16(paint)) {
+ return SkMask::kLCD16_Format;
+ }
+ }
+
#if defined(SK_SUPPORT_LCDTEXT)
if (flags & SkPaint::kLCDRenderText_Flag) {
return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ?
if (NULL == fMaskFilter &&
fRec.fMaskFormat != SkMask::kBW_Format &&
+ fRec.fMaskFormat != SkMask::kLCD16_Format &&
(fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
{
const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
/*
** Copyright 2006, The Android Open Source Project
**
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
**
- ** http://www.apache.org/licenses/LICENSE-2.0
+ ** http://www.apache.org/licenses/LICENSE-2.0
**
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <vector>
#include "SkString.h"
#include "SkPaint.h"
#include "SkFloatingPoint.h"
-
+#include "SkUtils.h"
// Is a font ID valid?
bool IsValid(SkFontID fontID);
-
-
+
+
// Get a font
CTFontRef GetFont(SkFontID fontID);
SkNativeFontInfo GetFontInfo(const SkString &theName, SkTypeface::Style theStyle);
-
-
+
+
// Create a font
SkNativeFontInfo CreateFont(const SkString &theName, SkTypeface::Style theStyle);
fontInfo.style = SkTypeface::kNormal;
fontInfo.fontID = kSkInvalidFontID;
fontInfo.fontRef = NULL;
-
+
mFonts.push_back(fontInfo);
}
if (theIter->name == theName && theIter->style == theStyle)
return(*theIter);
}
-
+
return(fontInfo);
}
private:
- CGColorSpaceRef mColorSpace;
+ CGColorSpaceRef mColorSpaceGray;
+ CGColorSpaceRef mColorSpaceRGB;
CGAffineTransform mTransform;
CTFontRef mFont;
// Initialise ourselves
- mColorSpace = CGColorSpaceCreateDeviceGray();
+// mColorSpaceRGB = CGColorSpaceCreateDeviceRGB();
+// mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
+ mColorSpaceGray = CGColorSpaceCreateDeviceGray();
+
const float inv = 1.0f / FONT_CANONICAL_POINTSIZE;
mTransform = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]) * inv,
-SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]) * inv,
{
// Clean up
- CFSafeRelease(mColorSpace);
+ CFSafeRelease(mColorSpaceGray);
+ CFSafeRelease(mColorSpaceRGB);
CFSafeRelease(mFont);
}
glyph->fLeft = sk_float_round2int(CGRectGetMinX(theBounds));
}
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
-{ CGContextRef cgContext;
+#include "SkColorPriv.h"
+
+static inline uint16_t rgb_to_lcd16(uint32_t rgb) {
+ int r = (rgb >> 16) & 0xFF;
+ int g = (rgb >> 8) & 0xFF;
+ int b = (rgb >> 0) & 0xFF;
+
+ // invert, since we draw black-on-white, but we want the original
+ // src mask values.
+ r = 255 - r;
+ g = 255 - g;
+ b = 255 - b;
+
+ return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
+}
+
+#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+ CGContextRef cgContext;
CGGlyph cgGlyph;
CGFontRef cgFont;
cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
cgFont = CTFontCopyGraphicsFont(mFont, NULL);
- cgContext = CGBitmapContextCreate( glyph.fImage, glyph.fWidth, glyph.fHeight, 8,
- glyph.rowBytes(), mColorSpace, kCGImageAlphaNone);
+
+ SkAutoSMalloc<1024> storage;
+
+ CGColorSpaceRef colorspace = mColorSpaceGray;
+ uint32_t info = BITMAP_INFO_GRAY;
+ void* image = glyph.fImage;
+ size_t rowBytes = glyph.rowBytes();
+ float grayColor = 1; // white
+
+ /* For LCD16, we first create a temp offscreen cg-context in 32bit,
+ * erase to white, and then draw a black glyph into it. Then we can
+ * extract the r,g,b values, invert-them, and now we have the original
+ * src mask components, which we pack into our 16bit mask.
+ */
+ if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+ colorspace = mColorSpaceRGB;
+ info = BITMAP_INFO_RGB;
+ // need tmp storage for 32bit RGB offscreen
+ rowBytes = glyph.fWidth << 2;
+ size_t size = glyph.fHeight * rowBytes;
+ image = storage.realloc(size);
+ // we draw black-on-white (and invert in rgb_to_lcd16)
+ sk_memset32((uint32_t*)image, 0xFFFFFFFF, size >> 2);
+ grayColor = 0; // black
+ }
+
+ cgContext = CGBitmapContextCreate(image, glyph.fWidth, glyph.fHeight, 8,
+ rowBytes, colorspace, info);
// Draw the glyph
if (cgFont != NULL && cgContext != NULL) {
- CGContextSetGrayFillColor( cgContext, 1.0, 1.0);
+ CGContextSetAllowsFontSubpixelQuantization(cgContext, true);
+ CGContextSetShouldSubpixelQuantizeFonts(cgContext, true);
+
+ CGContextSetGrayFillColor( cgContext, grayColor, 1.0);
CGContextSetTextDrawingMode(cgContext, kCGTextFill);
CGContextSetFont( cgContext, cgFont);
CGContextSetFontSize( cgContext, FONT_CANONICAL_POINTSIZE);
CGContextSetTextMatrix( cgContext, mTransform);
CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1);
+
+ if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+ // downsample from rgba to rgb565
+ int width = glyph.fWidth;
+ const uint32_t* src = (const uint32_t*)image;
+ uint16_t* dst = (uint16_t*)glyph.fImage;
+ size_t dstRB = glyph.rowBytes();
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd16(src[i]);
+ }
+ src = (const uint32_t*)((const char*)src + rowBytes);
+ dst = (uint16_t*)((char*)dst + dstRB);
+ }
+ }
}
// Clean up
case kCGPathElementCloseSubpath:
skPath->close();
break;
-
+
default:
SkASSERT("Unknown path element!");
break;
theSize = CFDataGetLength(cfData);
CFSafeRelease(cfData);
}
-
+
return(theSize);
}