ultimatepp/uppsrc/Geom/Draw/hrr.cpp
rylek f8e640c87f Transparent HRR generator fixes, Path+Area+MarkTool - better support for 'empty' styles
git-svn-id: svn://ultimatepp.org/upp/trunk@542 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-10-19 09:44:36 +00:00

1624 lines
43 KiB
C++
Raw Blame History

//////////////////////////////////////////////////////////////////////
// hrr: high resolution raster.
#include "GeomDraw.h"
#pragma hdrstop
#include "hrr.h"
#include <plugin/jpg/jpg.h>
#include <plugin/gif/gif.h>
#include <plugin/png/png.h>
NAMESPACE_UPP
#define LLOG(x) // LOG(x)
void RasterCopy(RasterEncoder& dest, Raster& src, const Rect& src_rc)
{
dest.Start(src_rc.Size());
for(int y = src_rc.top; y < src_rc.bottom; y++)
dest.WriteLine((const RGBA *)src.GetLine(y) + src_rc.left);
}
void ImageWriter::Open(ImageBuffer& output_, Point pos_, Rect clip_, bool merge_)
{
format.SetRGBA();
output = &output_;
pos = pos_;
clip = clip_;
merge = merge_;
}
void ImageWriter::Start(Size sz)
{
src_size = sz;
line = 0;
left = max(pos.x, clip.left);
width = max(min(pos.x + src_size.cx, clip.right) - left, 0);
offset = (width > 0 ? left - pos.x : 0);
}
void ImageWriter::WriteLineRaw(const byte *s)
{
if(line >= src_size.cy || width <= 0)
return;
int y = line++ + pos.y;
if(y >= clip.top && y < clip.bottom) {
const RGBA *l = (const RGBA *)s;
if(merge)
AlphaBlend(&(*output)[y][left], l + offset, width);
else
memcpy(&(*output)[y][left], l + offset, width * sizeof(RGBA));
}
}
Size ImageBufferRaster::GetSize()
{
return buffer.GetSize();
}
Raster::Info ImageBufferRaster::GetInfo()
{
Info info;
info.bpp = 32;
info.colors = 0;
info.dots = Null;
info.hotspot = Null;
info.kind = buffer.GetKind();
return info;
}
Raster::Line ImageBufferRaster::GetLine(int line)
{
return Line(buffer[line], false);
}
inline Stream& operator % (Stream& strm, Color& color)
{
dword dw = color.GetRaw();
strm % dw;
if(strm.IsLoading())
color = Color::FromRaw(dw);
return strm;
}
inline Stream& operator % (Stream& strm, Rectf& rc)
{
strm % rc.left % rc.top % rc.right % rc.bottom;
return strm;
}
static int64 Unpack64(dword i)
{
if(!(i & 0x80000000))
return i;
return int64(i & 0x7fffffff) << 8;
}
static dword CeilPack64(int64 i)
{
if(i < 0x7fffffff)
return (dword)i;
if(i < INT64(0x3fffffff00))
return (dword)((i + INT64(0x80000000ff)) >> 8);
return 0xffffffff;
}
One<StreamRaster> HRRInfo::GetDecoder() const
{
switch(method) {
case METHOD_JPG: return new JPGRaster;
case METHOD_GIF: return new GIFRaster;
case METHOD_PNG: return new PNGRaster;
default: return 0;
}
}
One<StreamRasterEncoder> HRRInfo::GetEncoder() const
{
switch(method) {
case METHOD_JPG: return new JPGEncoder(quality);
case METHOD_GIF: return new GIFEncoder;
case METHOD_PNG: return new PNGEncoder;
default: return 0;
}
}
/*
One<StreamRasterEncoder> HRR::StdCreateEncoder(const HRRInfo& info)
{
switch(info.GetMethod())
{
case HRRInfo::METHOD_JPG: return new JpgEncoder(info.GetQuality());
case HRRInfo::METHOD_GIF: return new GifEncoder;
case HRRInfo::METHOD_RLE: return new RleEncoder;
// case HRRInfo::METHOD_ZIM: return new ZImageEncoder;
#ifndef flagNOHRRPNG
case HRRInfo::METHOD_PNG: return new PngEncoder;
#endif
default: return 0;
}
}
*/
Vector<int> HRRInfo::EnumMethods()
{
Vector<int> out;
out << METHOD_JPG << METHOD_GIF /* << METHOD_RLE*/ << METHOD_PNG; // << METHOD_ZIM;
return out;
}
/*
enum { wAlphaBlend = 200 };
static void Mask1Blt(byte *dest, const byte *src, const byte *mask, int count)
{
while(count >= 4)
{
if(mask[0]) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; }
if(mask[1]) { dest[3] = src[3]; dest[4] = src[4]; dest[5] = src[5]; }
if(mask[2]) { dest[6] = src[6]; dest[7] = src[7]; dest[8] = src[8]; }
if(mask[3]) { dest[9] = src[9]; dest[10] = src[10]; dest[11] = src[11]; }
dest += 12;
src += 12;
mask += 4;
count -= 4;
}
if(count & 2)
{
if(mask[0]) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; }
if(mask[1]) { dest[3] = src[3]; dest[4] = src[4]; dest[5] = src[5]; }
dest += 6;
src += 6;
mask += 2;
}
if(count & 1)
if(mask[0]) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; }
}
*/
/*
static void Mask1Copy(PixelArray& dest, const PixelArray& src, const PixelArray& mask)
{
ASSERT(mask.bpp == 8 && src.bpp == 24 && dest.bpp == 24);
Size size = dest.GetSize();
ASSERT(src.GetSize() == size && mask.GetSize() == size);
for(int i = 0; i < size.cy; i++)
Mask1Blt(dest.GetUpScan(i), src.GetUpScan(i), mask.GetUpScan(i), size.cx);
}
*/
/*
static void StreamAlphaBlend(Stream& stream, Rect& dest, Rect& src, int& alpha,
AlphaArray& image, Color& blend_bgnd)
{
int version = 2;
stream / version / alpha;
Pack16(stream, dest);
Pack16(stream, src);
stream % image;
if(version >= 2)
stream % blend_bgnd;
else if(stream.IsLoading())
{
alpha = tabs(alpha);
blend_bgnd = Null;
}
}
*/
/*
static void DrawAlphaBlend(Draw& draw, Rect dest, Rect src, int alpha, AlphaArray& image, Color blend_bgnd)
{
ASSERT(alpha >= 0);
Rect clip = draw.GetClip(), dclip = dest & clip, dclip0 = dclip.Size();
if(dclip.IsEmpty() || alpha == 0)
return;
Color c0 = (image.pixel.palette.GetCount() >= 1 ? image.pixel.palette[0] : Color(Null));
Color c1 = (image.pixel.palette.GetCount() >= 2 ? image.pixel.palette[1] : Color(Null));
bool mono_pixel = (image.pixel.IsMono() && (IsNull(c0) || IsNull(c1)));
if(mono_pixel && IsNull(c0) && IsNull(c1))
return;
if(draw.IsDrawing())
{
StreamAlphaBlend(draw.DrawingOp(wAlphaBlend), dest, src, alpha, image, blend_bgnd);
return;
}
PixelArray in_blend;
if(alpha < 100 && IsNull(blend_bgnd))
{
#ifdef PLATFORM_WIN32
in_blend = ImageToPixelArray(DrawToImage(draw, dest), draw, -3);
#else
in_blend = DrawableToPixelArray(draw.GetDrawable(), dest, false, -3, 4);
#endif
}
bool resize = (src.Size() != dest.Size() || (dest.Size() != dclip.Size() && draw.Dots()));
if(mono_pixel)
{
if(resize)
{
PixelArray new_data = PixelArray::Mono(dclip.Size(), 8);
PixelCopyAntiAliasMaskOnly(new_data, dest - dclip.TopLeft(), image.pixel, src, false, false, dclip0);
new_data.palette = image.pixel.palette;
image.pixel = new_data;
src = dclip0;
dest = dclip;
}
if(!in_blend.IsEmpty())
{
PixelArray copy_blend;
copy_blend <<= in_blend;
PixelKillMask(copy_blend, image.pixel, Nvl(c0, c1), !IsNull(c0));
PixelAlphaBlend(copy_blend, src, in_blend, Point(0, 0), alpha);
copy_blend.Paint(draw, src, dest);
}
else
image.pixel.Paint(draw, src, dest, c0, c1);
return;
}
if(resize)
{ // scale image offhand
if(image.pixel.GetBPP() > 8)
PixelSetConvert(image.pixel, -3);
if(!image.HasAlpha())
{
PixelArray new_data(dclip.Size(), -3);
PixelCopyAntiAlias(new_data, dest - dclip.TopLeft(), image.pixel, src, dclip0);
image.pixel = new_data;
}
else
{
AlphaArray new_image(dclip.Size(), -3);
PixelCopyAntiAliasMaskOut(new_image, dest - dclip.TopLeft(), image, src, false, false, dclip0);
image = new_image;
}
src = dclip0;
dest = dclip;
}
if(!in_blend.IsEmpty())
{ // blend with display contents
if(image.HasAlpha())
{
PixelSetConvert(image.pixel, -3);
Mask1Copy(image.pixel, in_blend, image.alpha);
}
PixelAlphaBlend(image.pixel, src, in_blend, Point(0, 0), alpha);
image.pixel.Paint(draw, src, dest);
}
else {
if(alpha < 100)
PixelAlphaBlend(image.pixel, blend_bgnd, alpha, src);
if(image.HasAlpha())
image.Paint(draw, src, dest);
else
image.pixel.Paint(draw, src, dest);
}
// RTIMING("DrawAlphaBlend (raw)");
}
*/
/*
static void wsAlphaBlend(Draw& draw, Stream& stream, const DrawingPos& pos)
{
Rect src, dest;
int alpha;
AlphaArray image;
Color blend_bgnd;
StreamAlphaBlend(stream, dest, src, alpha, image, blend_bgnd);
DrawAlphaBlend(draw, pos(dest), src, alpha, image, blend_bgnd);
}
*/
//static DrawerRegistrator MK__s(wAlphaBlend, wsAlphaBlend);
static int GetMaskInfo(const RGBA *rgba, int count)
{
if(count == 0)
return 0;
if(rgba->a == 255) {
for(; count > 0 && rgba->a == 255; count--, rgba++)
;
return (count ? 2 : 1);
}
else if(rgba->a == 0) {
for(; count > 0 && rgba->a == 0; count--, rgba++)
;
return (count ? 2 : 0);
}
return 2;
}
static String EncodeMask(const ImageBuffer& mask, bool write_size)
{
StringBuffer out;
if(write_size) {
char temp[4];
Poke16le(temp + 0, mask.GetWidth());
Poke16le(temp + 2, mask.GetHeight());
out.Cat(temp, 4);
}
int full = out.GetLength();
Size size = mask.GetSize();
for(int i = 0; i < size.cy; i++) {
const RGBA *p = mask[size.cy - i - 1], *e = p + size.cx;
int start = out.GetLength();
while(p < e) {
bool init0 = false;
if(p->a < 128)
{ // full part
const RGBA *b = p;
while(++p < e && p->a < 128)
;
int n = p - b;
while(n > 253) {
out.Cat(255);
out.Cat(2);
n -= 253;
}
if(n > 0)
out.Cat(n + 2);
}
else
init0 = true;
if(p < e) {
const RGBA *b = p;
while(++p < e && p->a >= 128)
;
if(p < e) {
if(init0)
out.Cat(2);
int n = p - b;
while(n > 253) {
out.Cat(255);
out.Cat(2);
n -= 253;
}
if(n > 0)
out.Cat(n + 2);
}
}
}
if(out.GetLength() > start)
full = out.GetLength();
out.Cat(1);
}
if(full < out.GetLength())
out.SetLength(full);
return out;
}
static void DecodeMask(ImageBuffer& mask, String s, bool read_size)
{
Size size = mask.GetSize();
const byte *p = s;
if(read_size) {
size.cx = Peek16le(p);
size.cy = Peek16le(p + 2);
p += 4;
}
for(int i = 0; i < size.cy && *p; i++) {
RGBA *d = mask[size.cy - i - 1], *e = d + size.cx;
while(*p >= 2 && d < e) {
int n1 = *p++ - 2;
if(e - d <= n1) {
while(d < e)
d++->a = 0;
break;
}
RGBA *dd = d + n1;
while(d < dd)
d++->a = 0;
if(*p >= 2) {
n1 = *p++ - 2;
if(e - d <= n1)
break;
d += n1;
}
}
while(*p >= 2)
p++;
if(*p)
p++;
}
}
/*
static String EncodeMask(const RawImage& mask)
{
ASSERT(mask.bpp == 8);
String out;
int full = 0;
Size size = mask.GetSize();
for(int i = 0; i < size.cy; i++)
{
const byte *p = mask.GetUpScan(i), *e = p + size.cx;
int start = out.GetLength();
while(p < e)
{
const byte *b = p;
while(++p < e && *p)
;
if(p >= e)
break;
int n = p - b;
while(n > 253)
{
out.Cat(255);
out.Cat(2);
n -= 253;
}
out.Cat(n + 2);
b = p;
while(++p < e && !*p)
;
n = p - b;
while(n > 253)
{
out.Cat(255);
out.Cat(2);
n -= 253;
}
if(n > 0 || p < e)
out.Cat(n + 2);
}
if(out.GetLength() > start)
full = out.GetLength();
out.Cat(1);
}
if(full < out.GetLength())
out.Trim(full);
return out;
}
static void DecodeMask(RawImage& mask, const String& s)
{
ASSERT(mask.bpp == 8);
Size size = mask.GetSize();
mask.Set(1);
const byte *p = s;
for(int i = 0; i < size.cy && *p; i++)
{
byte *d = mask.GetUpScan(i), *e = d + size.cx;
while(*p >= 2 && d < e)
{
int n1 = *p++ - 2;
if(e - d <= n1)
break;
d += n1;
if(*p < 2)
break;
n1 = *p++ - 2;
if(e - d <= n1)
{
memset(d, 0, e - d);
break;
}
memset(d, 0, n1);
d += n1;
}
while(*p >= 2)
p++;
if(*p)
p++;
}
}
*/
HRRInfo::HRRInfo()
: levels(0)
, log_rect(0, 0, 0, 0)
, map_rect(0, 0, 0, 0)
, background(White)
, method(METHOD_JPG)
, quality(50)
, mono(false)
, mono_black(Black)
, mono_white(White)
{
}
HRRInfo::HRRInfo(Rectf log_rect, int levels, Color background, int method, int quality,
bool mono, Color mono_black, Color mono_white)
: log_rect(log_rect), levels(levels), background(background), method(method), quality(quality)
, mono(mono), mono_black(mono_black), mono_white(mono_white)
{
double wadd = log_rect.Height() - log_rect.Width();
map_rect = log_rect;
if(wadd >= 0)
map_rect.right += wadd;
else
map_rect.top += wadd;
}
void HRRInfo::Serialize(Stream& stream)
{
int outver = (stream.IsStoring() && !mono && method != METHOD_ZIM && method != METHOD_BZM ? 4 : 5);
int version = StreamHeading(stream, outver, 2, 5, "HRRInfo");
if(version >= 2)
stream / levels % background % log_rect % map_rect;
if(version >= 3)
stream / method;
else if(stream.IsLoading())
method = METHOD_JPG;
if(version >= 4)
stream / quality;
else if(stream.IsLoading())
quality = 0;
if(version >= 5)
stream % mono % mono_black % mono_white;
else if(stream.IsLoading())
{
mono = false;
mono_black = Null;
mono_white = Null;
}
}
Vector<int> HRRInfo::EnumQualities(int method)
{
Vector<int> out;
switch(method)
{
case METHOD_JPG:
{
for(int i = 10; i <= 100; i += 10)
out << i;
}
break;
case METHOD_GIF:
case METHOD_RLE:
case METHOD_PNG:
case METHOD_ZIM:
case METHOD_BZM:
out << 0;
break;
default:
NEVER();
break;
}
return out;
}
VectorMap<int, String> HRRInfo::GetPackMap()
{
VectorMap<int, String> out;
Vector<int> methods = EnumMethods();
for(int m = 0; m < methods.GetCount(); m++)
{
Vector<int> qualities = EnumQualities(methods[m]);
if(qualities.IsEmpty())
qualities.Add(0);
for(int q = 0; q < qualities.GetCount(); q++)
out.FindAdd(Pack(methods[m], qualities[q]), GetName(methods[m], qualities[q]));
}
return out;
}
String HRRInfo::GetName(int method, int quality)
{
String out;
switch(method)
{
case METHOD_JPG:
out << "JPEG " << (quality ? quality : DFLT_JPG_QUALITY) << "%";
break;
case METHOD_GIF:
out << "GIF";
break;
/*
case METHOD_RLE:
out << "RLE";
break;
*/
case METHOD_PNG:
out << "PNG";
break;
/*
case METHOD_ZIM:
out << "ZIM";
break;
case METHOD_BZM:
out << "BZM";
break;
*/
default:
out << "?? (" << method << ")";
}
return out;
}
double HRRInfo::GetEstimatedFileSize(int _levels, int method, int quality)
{
int images = 0;
for(int i = 0; i < _levels; i++)
images += 1 << (2 * i);
int dir_size = images * sizeof(int) // offset table
+ 256; // estimated heading size
double data_size = images * double(UNIT * UNIT);
switch(method)
{
case METHOD_JPG:
data_size *= (quality ? quality : DFLT_JPG_QUALITY) / 400.0; // guessed JPEG size
break;
case METHOD_GIF:
data_size /= 2;
break;
case METHOD_RLE:
data_size /= 1.5;
break;
case METHOD_PNG:
data_size /= 1.6;
break;
case METHOD_ZIM:
data_size /= 1.6;
break;
case METHOD_BZM:
data_size /= 1.8;
break;
default:
NEVER();
break;
}
return data_size;
}
//////////////////////////////////////////////////////////////////////
// HRR::Block::
void HRR::Block::Init(Size s, RGBA color)
{
// static TimingInspector ti("HRR::Block::Init");
// ti.Start();
size = s;
block.Create(size);
Fill(~block, color, block.GetLength());
}
//////////////////////////////////////////////////////////////////////
// HRR::
One<StreamRaster> (*HRR::CreateDecoder)(const HRRInfo& info) = &HRR::StdCreateDecoder;
One<StreamRasterEncoder> (*HRR::CreateEncoder)(const HRRInfo& info) = &HRR::StdCreateEncoder;
static const Size SUNIT(HRRInfo::UNIT, HRRInfo::UNIT);
static const Rect RUNIT(0, 0, HRRInfo::UNIT, HRRInfo::UNIT);
HRR::HRR()
{
cache_sizeof_limit = DEFAULT_CACHE_SIZEOF_LIMIT;
}
HRR::HRR(const char *path, bool read_only)
{
Open(path, read_only);
}
One<StreamRaster> HRR::StdCreateDecoder(const HRRInfo& info)
{
switch(info.GetMethod()) {
case HRRInfo::METHOD_GIF: return new GIFRaster;
case HRRInfo::METHOD_PNG: return new PNGRaster;
case HRRInfo::METHOD_JPG: return new JPGRaster;
// case HRRInfo::METHOD_BMP: return new BMPRaster;
}
return NULL;
}
One<StreamRasterEncoder> HRR::StdCreateEncoder(const HRRInfo& info)
{
switch(info.GetMethod()) {
case HRRInfo::METHOD_GIF: return new GIFEncoder;
case HRRInfo::METHOD_PNG: return new PNGEncoder;
case HRRInfo::METHOD_JPG: return new JPGEncoder(info.GetQuality());
// case HRRInfo::METHOD_BMP: return new BMPEncoder;
}
return NULL;
}
bool HRR::Open(const char *path, bool read_only)
{
Close();
if(!path || !*path || !stream.Open(path, read_only ? stream.READ : stream.READWRITE))
return false;
stream.SetLoading();
Serialize();
if(stream.IsError() || info.levels <= 0
|| info.map_rect.Width() <= 0 || info.map_rect.Height() <= 0)
{
Close();
return false;
}
return true;
}
void HRR::Close()
{
stream.Close();
pixel_directory.Clear();
mask_directory.Clear();
image_cache.Clear();
directory_sizeof = 0;
cache_sizeof = 0;
info = HRRInfo();
}
static int GetImageSize(Size sz)
{
return sizeof(Image) + 32 + sz.cx * sz.cy * sizeof(RGBA);
}
inline static int GetImageSize(const Image& im) { return GetImageSize(im.GetSize()); }
void HRR::FlushCache(int limit)
{
while(!image_cache.IsEmpty() && cache_sizeof > limit) {
cache_sizeof -= GetImageSize(image_cache[0]);
image_cache.Remove(0);
}
}
void HRR::ClearCache()
{
image_cache.Clear();
cache_sizeof = 0;
}
static Size GetLogBlockSize(Rectf box_rc, Rectf map_rc)
{
Size part_size(HRRInfo::UNIT, HRRInfo::UNIT);
if(box_rc.left >= map_rc.right)
return Size(0, 0);
if(box_rc.right >= map_rc.right)
part_size.cx = fround(part_size.cx * (map_rc.right - box_rc.left) / box_rc.Width());
if(box_rc.bottom <= map_rc.top)
return Size(0, 0);
else if(box_rc.top < map_rc.top)
part_size.cy = fround(part_size.cy * (box_rc.bottom - map_rc.top) / box_rc.Height());
return part_size;
}
static Color BlendColor(Color a, int percent, Color b)
{
return Color(
b.GetR() + iscale(a.GetR() - b.GetR(), percent, 100),
b.GetG() + iscale(a.GetG() - b.GetG(), percent, 100),
b.GetB() + iscale(a.GetB() - b.GetB(), percent, 100));
}
static int StopMsec(int start = 0)
{
return GetTickCount() - start;
}
static void DrawAlphaImage(Draw& draw, Rect dest, Image img, Rect src, int alpha)
{
if(alpha <= 0)
return;
alpha += alpha >> 7;
if(alpha >= 256) {
draw.DrawImage(dest, img, src);
return;
}
Size outsz = min(src.Size(), dest.Size());
ImageBuffer temp(outsz);
Rescale(ImageWriter(temp, false), outsz, ImageRaster(img), src);
for(RGBA *p = ~temp, *e = ~temp + temp.GetLength(); p < e; p++) {
// int a = (p->a + (p->a >> 7)) * alpha;
// p->r = p->r * a >> 16;
// p->g = p->g * a >> 16;
// p->b = p->b * a >> 16;
p->a = p->a * alpha >> 8;
}
// temp.SetKind(IMAGE_PREMULTIPLIED);
draw.DrawImage(dest, Image(temp));
}
void HRR::Paint(Draw& draw, Rect dest, Rectf src,
int alpha, int max_pixel, Color mono_black, Color mono_white, Color blend_bgnd)
{
LLOG("HRR::Paint: alpha = " << alpha
<< ", max_pixel = " << max_pixel << ", mono_black = " << mono_black
<< ", mono_white = " << mono_white << ", blend_bgnd = " << blend_bgnd
<< ", dest = " << dest << ", src = " << src << BeginIndent);
Swap(dest.top, dest.bottom);
draw.Clip(dest);
Paint(draw, MatrixfScale(src, dest), Null, alpha, max_pixel, mono_black, mono_white, blend_bgnd);
draw.End();
LLOG(EndIndent << "// HRR::Paint");
}
HRR::Cursor::Cursor(HRR& owner_, const Rectf& extent_, double measure_,
int alpha_, Color mono_black_, Color mono_white_, Color blend_bgnd_)
: owner(owner_)
, extent(extent_)
, measure(measure_)
, alpha(alpha_)
, mono_black(mono_black_)
, mono_white(mono_white_)
, blend_bgnd(blend_bgnd_)
{
bool use_pixel = (IsNull(mono_black) && IsNull(mono_white));
if(owner.info.IsMono() && use_pixel) {
mono_black = owner.info.GetMonoBlack();
mono_white = owner.info.GetMonoWhite();
use_pixel = (IsNull(mono_black) && IsNull(mono_white));
if(use_pixel) {
LLOG(EndIndent << "//HRR::Paint, null color, empty case");
return;
}
}
// bool use_bg = (alpha < 100 && IsNull(blend_bgnd) || do_transform);
// bool use_alpha = !use_pixel || IsNull(info.background);
bool is_bw = (!IsNull(mono_black) && !IsNull(mono_white));
// bool out_pixel = (use_pixel || is_bw);
// bool out_alpha = (use_pixel ? IsNull(info.background) : !is_bw);
// LLOG("[" << StopMsec(ticks) << "] use_bg = " << use_bg << ", use_pixel = " << use_pixel << ", use_alpha = " << use_alpha
// << ", is_bw = " << is_bw << ", out_pixel = " << out_pixel << ", out_alpha = " << out_alpha);
double r = HRRInfo::UNIT / owner.info.GetMapRect().Width();
// if(draw.Dots())
// r /= 5; // ad hoc conversion from screen space to dot resolution
level = 0;
for(; level < owner.info.GetLevels() - 1 && r < measure; r *= 2, level++)
;
// DUMP(level);
if(!IsNull(mono_black))
mono_black = BlendColor(mono_black, alpha, Nvl(owner.info.GetBackground(), White));
if(!IsNull(mono_white))
mono_white = BlendColor(mono_white, alpha, Nvl(owner.info.GetBackground(), White));
// calculate interest area in Q-tree blocks
total = 1 << level;
Rectf blocks = (extent - owner.info.GetMapRect().BottomLeft()) / owner.info.GetMapRect().Size() * double(total);
rc = Rect(ffloor(blocks.left), ffloor(-blocks.bottom), fceil(blocks.right), fceil(-blocks.top));
rc &= Rect(0, 0, total, total);
// prepare clipping & image loader
if(!owner.info.IsMono()) {
raster = CreateDecoder(owner.info);
if(!raster) {
LLOG(EndIndent << "//HRR:x: decoder not found, exiting");
return;
}
}
// adjust transform parameters to convert from Q-tree space to device coords
// delta += info.map_rect.BottomLeft() * scale;
// scale *= Sizef(1, -1) * info.map_rect.Size() / double(1 << level);
#ifdef _DEBUG
// int ti = 0;
#endif
block = rc.TopLeft();
block.x--;
}
bool HRR::Cursor::Fetch(Rectf& part)
{
for(;;) {
if(++block.x >= rc.right) {
block.x = rc.left;
if(++block.y >= rc.bottom)
return false;
}
LLOG("[" << StopMsec(ticks) << "] block = [" << x << ", " << y << "]");
int pixel_offset = owner.pixel_directory[level][block.x + block.y * total];
int mask_offset = owner.mask_directory[level][block.x + block.y * total];
int coff;
if(pixel_offset)
coff = pixel_offset;
else if(mask_offset)
coff = -mask_offset;
else
continue;
int cimg = -1;
if((cimg = owner.image_cache.Find(coff)) < 0) {
ImageBuffer new_image;
if(pixel_offset) {
owner.stream.Seek(Unpack64(pixel_offset));
new_image = raster->Load(owner.stream);
if(new_image.IsEmpty()) {
RLOG(NFormat("Failed to load block [%d, %d].", block.x, block.y));
continue;
}
// PixelSetConvert(new_image.pixel, -3);
}
if(mask_offset) {
owner.stream.Seek(Unpack64(mask_offset));
int len = owner.stream.GetIL();
ASSERT(len >= 0 && len < HRRInfo::UNIT * (HRRInfo::UNIT + 1) + 1);
StringBuffer databuf(len);
owner.stream.Get(databuf, len);
String data = databuf;
if(owner.version < 5) {
Size sz(0, 0);
if(cimg >= 0)
sz = new_image.GetSize();
else if(pixel_offset) {
int csize = owner.size_cache.Find(pixel_offset);
if(csize < 0) {
if(owner.size_cache.GetCount() >= 10000)
owner.size_cache.Clear();
int64 pixpos = Unpack64(pixel_offset);
if(pixpos > owner.stream.GetSize())
owner.stream.SetSize(pixpos);
// stream.Seek(pixpos);
csize = owner.size_cache.GetCount();
// Stream64Stream pixel_stream(stream, pixpos);
owner.stream.Seek(pixpos);
raster->Open(owner.stream);
owner.size_cache.Add(pixel_offset, raster->GetSize());
}
sz = owner.size_cache[csize];
}
if(sz.cx <= 0 || sz.cy <= 0)
continue;
// new_image.alpha = PixelArray::Mono(sz);
}
DecodeMask(new_image, data, owner.version >= 5);
}
int new_len = new_image.GetLength() * sizeof(RGBA);
owner.FlushCache(owner.cache_sizeof_limit - new_len);
owner.cache_sizeof += new_len;
cimg = owner.image_cache.GetCount();
owner.image_cache.Add(coff, new_image);
}
if(cimg >= 0) {
part = owner.GetLogBlockRect(level, RectC(block.x, block.y, 1, 1));
Size sz = owner.image_cache[cimg].GetSize();
part.right = part.left + part.Width() * sz.cx / HRRInfo::UNIT;
part.top = part.bottom - part.Height() * sz.cy / HRRInfo::UNIT;
return true;
}
}
}
Image HRR::Cursor::Get()
{
ASSERT(cimg >= 0);
return owner.image_cache[cimg];
}
void HRR::Paint(Draw& draw, const Matrixf& trg_pix, GisTransform transform,
int alpha, int max_pixel, Color mono_black, Color mono_white, Color blend_bgnd)
{
LLOG("HRR::Paint: alpha = " << alpha
<< ", max_pixel = " << max_pixel << ", mono_black = " << mono_black
<< ", mono_white = " << mono_white << ", blend_bgnd = " << blend_bgnd
<< ", trg_pix = " << trg_pix << BeginIndent);
int ticks = StopMsec();
ASSERT(alpha >= 0);
if(alpha == 0 || info.IsEmpty() || !IsOpen()) {
LLOG(EndIndent << "//HRR::Paint, empty case");
return;
}
bool do_transform = !transform.IsIdentity();
bool is_straight = !do_transform && fabs(trg_pix.x.y) <= 1e-10 && fabs(trg_pix.y.x) <= 1e-10;
bool use_pixel = (IsNull(mono_black) && IsNull(mono_white));
if(info.mono && use_pixel) {
mono_black = info.mono_black;
mono_white = info.mono_white;
use_pixel = (IsNull(mono_black) && IsNull(mono_white));
if(use_pixel) {
LLOG(EndIndent << "//HRR::Paint, null color, empty case");
return;
}
}
bool use_bg = (alpha < 100 && IsNull(blend_bgnd) || do_transform);
bool use_alpha = !use_pixel || IsNull(info.background);
bool is_bw = (!IsNull(mono_black) && !IsNull(mono_white));
bool out_pixel = (use_pixel || is_bw);
bool out_alpha = (use_pixel ? IsNull(info.background) : !is_bw);
LLOG("[" << StopMsec(ticks) << "] use_bg = " << use_bg << ", use_pixel = " << use_pixel << ", use_alpha = " << use_alpha
<< ", is_bw = " << is_bw << ", out_pixel = " << out_pixel << ", out_alpha = " << out_alpha);
Matrixf pix_trg = MatrixfInverse(trg_pix);
Rect clip = draw.GetClip();
Rectf csrc = info.log_rect & transform.SourceExtent(Rectf(clip) * pix_trg);
// Pointf scale = Sizef(1, -1) * Sizef(dest.Size()) / Sizef(src.Size());
// Pointf delta = Pointf(dest.TopLeft()) - src.BottomLeft() * scale;
// Rectf csrc = src & info.log_rect;
Rect cdest = RectfToRect(transform.TargetExtent(csrc) * trg_pix) & clip;
// Rect cdest = RectfToRect(csrc * scale + delta);
// Swap(cdest.top, cdest.bottom);
// DrawRectMinusRect(draw, dest, cdest, info.background);
if(cdest.IsEmpty())
{ // intersection is less than 1 pixel wide / high
LLOG(EndIndent << "//HRR::Paint: empty destination, exiting");
return;
}
double r = fpmax(Sizef(cdest.Size()) * Sizef(info.map_rect.Size()) / csrc.Size()) / info.UNIT;
if(draw.Dots())
r /= 5; // ad hoc conversion from screen space to dot resolution
int level = 0;
for(; level < info.levels - 1 && r > max_pixel; r /= 2, level++)
;
// DUMP(level);
if(!IsNull(mono_black))
mono_black = BlendColor(mono_black, alpha, Nvl(info.background, White));
if(!IsNull(mono_white))
mono_white = BlendColor(mono_white, alpha, Nvl(info.background, White));
ImageBuffer out_blend;
if(use_bg) {
out_blend.Create(cdest.Size());
Fill(out_blend, info.background, out_blend.GetLength());
}
LOG("out blend: " << out_blend.GetSize());
// calculate interest area in Q-tree blocks
int total = 1 << level;
Rectf blocks = (csrc - info.map_rect.BottomLeft()) / info.map_rect.Size() * double(total);
Rect rc(ffloor(blocks.left), ffloor(-blocks.bottom), fceil(blocks.right), fceil(-blocks.top));
rc &= Rect(0, 0, total, total);
// prepare clipping & image loader
draw.Clip(cdest);
One<StreamRaster> decoder;
if(!info.mono) {
decoder = CreateDecoder(info);
if(decoder.IsEmpty()) {
draw.DrawText(cdest.left, cdest.top,
String().Cat() << "Unsupported HRR encoding: " << info.GetMethod(), StdFont());
draw.End();
LLOG(EndIndent << "//HRR:x: encoder not found, exiting");
return;
}
}
// adjust transform parameters to convert from Q-tree space to device coords
// delta += info.map_rect.BottomLeft() * scale;
// scale *= Sizef(1, -1) * info.map_rect.Size() / double(1 << level);
#ifdef _DEBUG
// int ti = 0;
#endif
SegmentTreeInfo seginfo;
seginfo.src_trg = transform;
seginfo.trg_pix = trg_pix;
seginfo.trg_pix.a -= cdest.TopLeft();
seginfo.antialias = true;
seginfo.branch = 0;
seginfo.max_depth = HRRInfo::HALF_BITS - 1;
double trg_dv = 2 / sqrt(fabs(Determinant(trg_pix)));
Rect rclip = clip - cdest.TopLeft();
Font err_font = StdFont();
for(int y = rc.top; y < rc.bottom; y++)
for(int x = rc.left; x < rc.right; x++)
{
LLOG("[" << StopMsec(ticks) << "] block = [" << x << ", " << y << "]");
seginfo.img_src = GetPixMapMatrix(level, x, y);
seginfo.img_src.x.x /= HRRInfo::UNIT;
seginfo.img_src.y.y /= HRRInfo::UNIT;
Matrixf src_img = MatrixfInverse(seginfo.img_src);
Rect src = RectfToRect((RUNIT * seginfo.img_src & csrc) * src_img).Inflated(2) & RUNIT;
Rectf map = src * seginfo.img_src;
Rect dest = (transform.TargetExtent(map) * trg_pix).Inflated(1) & Rectf(clip);
// Rect dest = RectfToRect(Rectf(x, y, x + 1, y + 1) * scale + delta);
// Rect clip = dest & draw.GetClip();
if(dest.IsEmpty())
continue;
Rect rdest = dest - cdest.TopLeft();
LinearSegmentTree tleft, ttop, tright, tbottom;
PlanarSegmentTree tplanar;
if(!is_straight) {
seginfo.max_deviation = trg_dv;
tleft = CreateLinearTree(src.TopLeft(), src.BottomLeft(), seginfo);
ttop = CreateLinearTree(src.TopLeft(), src.TopRight(), seginfo);
tright = CreateLinearTree(src.TopRight(), src.BottomRight(), seginfo);
tbottom = CreateLinearTree(src.BottomLeft(), src.BottomRight(), seginfo);
tplanar = CreatePlanarTree(tleft, ttop, tright, tbottom, seginfo);
}
// Rect src = (clip - dest.TopLeft()) * SUNIT / dest.Size();
// src.Inflate(2);
// src &= RUNIT;
int pixel_offset = pixel_directory[level][x + y * total];
int cimg = -1;
bool newimg = false;
if(pixel_offset && use_pixel && (cimg = image_cache.Find(pixel_offset)) < 0) {
newimg = true;
// Stream64Stream pixel_stream(stream, Unpack64(pixel_offset));
stream.Seek(Unpack64(pixel_offset));
Image ni = decoder->Load(stream);
if(ni.IsEmpty()) {
String warn = NFormat("Failed to load block [%d, %d].", x, y);
Size sz = GetTextSize(warn, err_font);
draw.DrawRect(Rect(dest.CenterPoint(), Size(1, 1)).Inflated(sz + 2), Color(255, 192, 192));
draw.DrawText((dest.left + dest.right - sz.cx) >> 1, (dest.top + dest.bottom - sz.cy) >> 1,
warn, StdFont(), Black);
continue;
}
cimg = image_cache.GetCount();
FlushCache(cache_sizeof_limit - GetImageSize(ni));
cache_sizeof += GetImageSize(ni);
cimg = image_cache.GetCount();
image_cache.Add(pixel_offset) = ni;
#ifdef _DEBUG
// static int part_id = 0;
// JpgEncoder().SaveArrayFile(Format("h:\\temp\\part%d.jpg", ++part_id), new_image);
#endif
// if(!out_blend.IsEmpty())
// PixelSetConvert(new_image.pixel, -3);
}
if(newimg) {
int mask_offset = mask_directory[level][x + y * total];
int cmask = -1;
if(mask_offset && use_alpha && (cmask = image_cache.Find(-mask_offset)) < 0) {
Size sz(0, 0);
stream.Seek(Unpack64(mask_offset));
int len = stream.GetIL();
ASSERT(len >= 0 && len < HRRInfo::UNIT * (HRRInfo::UNIT + 1) + 1);
StringBuffer data(len);
stream.Get(data, len);
// String s = data;
if(version < 5) {
if(cimg >= 0)
sz = image_cache[cimg].GetSize();
else {
if(!pixel_offset)
continue;
int csize = size_cache.Find(pixel_offset);
if(csize < 0) {
if(size_cache.GetCount() >= 10000)
size_cache.Clear();
int64 pixpos = Unpack64(pixel_offset);
if(pixpos > stream.GetSize())
stream.SetSize(pixpos);
// stream.Seek(pixpos);
csize = size_cache.GetCount();
// Stream64Stream pixel_stream(stream, pixpos);
stream.Seek(pixpos);
Size sz = 0;
if(decoder->Open(stream))
sz = decoder->GetSize();
size_cache.Add(pixel_offset, sz);
}
sz = size_cache[csize];
}
}
if(sz.cx > 0 && sz.cy > 0) {
if(cimg < 0) {
FlushCache(cache_sizeof_limit - GetImageSize(sz));
cmask = image_cache.GetCount();
cache_sizeof += GetImageSize(sz);
ImageBuffer ibuf(sz);
Fill(~ibuf, RGBAZero(), ibuf.GetLength());
DecodeMask(ibuf, data, version >= 5);
image_cache.Add(-mask_offset) = Image(ibuf);
}
else {
ImageBuffer ibuf(image_cache[cimg]);
DecodeMask(ibuf, data, version >= 5);
image_cache[cimg] = ibuf;
}
}
}
if(cimg < 0)
cimg = cmask;
}
if(cimg < 0) {
LLOG("[" << StopMsec(ticks) << "] pixel off, mask off");
if(!is_straight && !IsNull(info.background))
AlphaTransformPaint(out_blend, Image(), tplanar, tleft, ttop, tright, tbottom, seginfo, info.background);
else if(use_pixel)
draw.DrawRect(dest, info.background);
}
else {
const Image& img = image_cache[cimg];
if(!use_bg) {
LLOG("[" << StopMsec(ticks) << "] !use_bg -> direct mask blend");
if(alpha >= 100)
draw.DrawImage(dest, img, src);
else
DrawAlphaImage(draw, dest, img, src, minmax(alpha * 256 / 100, 0, 255));
// DrawAlphaBlend(draw, dest, src, 100, out_part, blend_bgnd);
}
else if(!is_straight) {
LLOG("[" << StopMsec(ticks) << "] use_bg -> twisted mask blend");
AlphaTransformPaint(out_blend, img,
tplanar, tleft, ttop, tright, tbottom, seginfo, LtRed());
}
else {
LLOG("[" << StopMsec(ticks) << "] use_bg -> buffered colored mask blend");
ImageWriter writer(out_blend, rdest.TopLeft(), rclip);
Rescale(writer, rdest.Size(), ImageRaster(img), src);
}
}
}
if(use_bg) {
if(alpha < 100) {
int coef = alpha * 255 / 100;
for(RGBA *p = ~out_blend, *e = p + out_blend.GetLength(); p < e; p++)
p->a = (p->a * coef) >> 8;
}
draw.DrawImage(cdest, out_blend);
}
draw.End();
LLOG(EndIndent << "[" << StopMsec(ticks) << "] //HRR::Paint");
}
int HRR::GetProgressCount(int levels, bool downscale)
{
ASSERT(levels > 0);
int images = 0;
if(downscale)
images = 1 << (2 * (levels - 1));
else
for(int i = 0; i < levels; i++)
images += 1 << (2 * i);
return images;
}
bool HRR::Create(const HRRInfo& _info, const char *path)
{
ASSERT(_info.levels > 0);
Close();
if(!stream.Open(path, stream.CREATE))
return false;
info = _info;
map_offset = 0;
Serialize();
return true;
}
static void StreamHRRString(Stream& stream, String& string)
{
int version = 1, len = string.GetLength();
stream / version / len;
if(version > 1 || stream.IsLoading() && (unsigned)len > stream.GetLeft())
{
stream.SetError();
return;
}
if(stream.IsStoring())
stream.SerializeRaw((byte *)(const char *)string, len);
else {
StringBuffer stringbuf(len);
stream.SerializeRaw((byte *)~stringbuf, len);
string = stringbuf;
}
}
void HRR::Serialize()
{
int outver = (stream.IsStoring() && (info.mono || info.method >= info.METHOD_ZIM) ? 5 : 4);
version = StreamHeading(stream, outver, 1, 5, "\r\n"
"High-Resolution Raster\r\n"
"Copyright <20>1999 Cybex Development, spol. s r.o.\r\n");
if(version >= 1)
stream % info;
if(version >= 2)
stream % map_offset;
else
map_offset = 0;
if(version >= 1)
{
if(stream.IsLoading() || pixel_directory.IsEmpty() || mask_directory.IsEmpty())
{
directory_sizeof = 0;
pixel_directory.Clear();
pixel_directory.SetCount(info.levels);
for(int i = 0; i < info.levels; i++)
{
int c = 1 << (2 * i);
pixel_directory[i].SetCount(c, 0);
directory_sizeof += c * 2 * sizeof(int);
}
mask_directory <<= pixel_directory;
}
if(version <= 3 || !info.mono)
for(int l = 0; l < info.levels; l++)
stream.SerializeRaw((byte *)pixel_directory[l].Begin(),
sizeof(pixel_directory[0][0]) * pixel_directory[l].GetCount());
if(version >= 3 && (IsNull(info.background) || info.mono))
for(int m = 0; m < info.levels; m++)
stream.SerializeRaw((byte *)mask_directory[m].Begin(),
sizeof(mask_directory[0][0]) * mask_directory[m].GetCount());
}
if(map_offset && version > 3)
{
int64 mappos = Unpack64(map_offset);
if(stream.IsStoring() && stream.GetSize() < mappos) {
stream.Seek(stream.GetSize());
stream.Put(0, (int)(mappos - stream.GetSize()));
}
if(stream.IsStoring() || mappos >= 0 && mappos < stream.GetSize()) {
stream.Seek(mappos);
int count = map.GetCount();
stream / count;
for(int i = 0; i < count; i++)
{
String key;
String val;
if(stream.IsStoring())
{
key = map.GetKey(i);
val = map[i];
}
StreamHRRString(stream, key);
StreamHRRString(stream, val);
if(stream.IsLoading())
map.Add(key, val);
}
}
}
else
map.Clear();
}
void HRR::Write(Writeback drawback, bool downscale)
{
ASSERT(stream.IsOpen());
if(map_offset > 0)
{
int64 mu = Unpack64(map_offset);
stream.Seek(mu);
stream.SetSize(mu);
map_offset = 0;
}
One<StreamRasterEncoder> encoder = CreateEncoder(info);
if(!encoder)
throw Exc(String().Cat() << "Unsupported HRR encoding: " << info.GetMethod());
bool abort = false;
try
{
Write(drawback, downscale, 0, 0, 0, *encoder, 0);
}
catch(AbortExc)
{
abort = true;
}
map_offset = CeilPack64(stream.GetPos());
stream.Seek(0);
Serialize(); // update header
if(abort)
throw AbortExc();
}
Matrixf HRR::GetPixMapMatrix(int level, int x, int y) const
{
double fac = 1 << level;
double xx = info.map_rect.Width() / fac, yy = -info.map_rect.Height() / fac;
return Matrixf(xx, 0, 0, yy, info.map_rect.left + xx * x, info.map_rect.bottom + yy * y);
}
int64 HRR::GetFileWriteSize() const
{
ASSERT(stream.IsOpen());
return stream.GetSize();
}
Rectf HRR::GetLogBlockRect(int level, const Rect& rc) const
{
return Rectf(rc) * GetPixMapMatrix(level, 0, 0);
/* Rectf r(rc);
double fac = 1.0 / (1 << level);
r = r * fac;
double t = r.bottom; r.bottom = 1 - r.top; r.top = 1 - t;
return r * info.map_rect.Size() + info.map_rect.TopLeft();
*/
}
bool HRR::Write(Writeback drawback, bool downscale, int level, int px, int py,
StreamRasterEncoder& format, Block *put)
{
static const Size SUNIT(info.UNIT, info.UNIT);
Block block(*this);
// TIMING("HRR::Write");
if(level >= info.levels - info.LCOUNT)
{ // generate all at once
// TIMING("HRR::Write(short step)");
// static TimingInspector __part("HRR::Write(part)");
// __part.Start();
int count = info.levels - level - 1;
// step & render individual images
block.Init(SUNIT << count, info.background);
// __part.End();
block.level = level + count;
block.area = RectC(px << count, py << count, 1 << count, 1 << count);
block.log_area = GetLogBlockRect(block.level, block.area);
bool done = drawback(block);
if(!done && downscale)
return false;
while(count >= 0)
{
int n = 1 << count;
for(Size a(0, 0); a.cy < n; a.cy++)
for(a.cx = 0; a.cx < n; a.cx++)
{
Point src = a * info.UNIT;
Size part_size = GetLogBlockSize(GetLogBlockRect(level + count, RectC(a.cx, a.cy, 1, 1)), info.log_rect);
if(part_size.cx <= 0 || part_size.cy <= 0)
continue;
ImageBuffer part(part_size);
RasterCopy(ImageWriter(part, false), ImageBufferRaster(block.block), Rect(src, part_size));
int lin = (int)((px << count) + a.cx + (((py << count) + a.cy) << (count + level)));
// TIMING("HRR::Write / save (direct)");
if(info.mono || IsNull(info.background)) {
int kind = GetMaskInfo(~part, part.GetLength());
if(kind && !info.mono) {
int pixoff = CeilPack64(stream.GetPos());
pixel_directory[level + count][lin] = pixoff;
int64 pixpos = Unpack64(pixoff);
if(stream.GetSize() < pixpos)
stream.Put(0, (int)(pixpos - stream.GetSize()));
stream.Seek(pixpos);
// Stream64Stream pixstream(stream, pixpos);
format.Save(stream, ImageBufferRaster(part));
}
if(kind == 2 || (kind == 1 && info.mono)) {
String s = EncodeMask(part, version >= 5);
ASSERT(s.GetLength() >= 4);
int maskoff = CeilPack64(stream.GetPos());
mask_directory[level + count][lin] = maskoff;
int64 maskpos = Unpack64(maskoff);
if(stream.GetSize() < maskpos)
stream.Put(0, (int)(maskpos - stream.GetSize()));
stream.Seek(maskpos);
stream.PutIL(s.GetLength());
stream.Put(s, s.GetLength());
}
}
else
{
int pixoff = CeilPack64(stream.GetPos());
pixel_directory[level + count][lin] = pixoff;
int64 pixpos = Unpack64(pixoff);
if(stream.GetSize() < pixpos)
stream.Put(0, (int)(pixpos - stream.GetSize()));
stream.Seek(pixpos);
// Stream64Stream pixstream(stream, pixpos);
format.Save(stream, ImageBufferRaster(part));
}
}
if(--count >= 0) // reduce image
if(downscale) {
Size sz = SUNIT << count;
ImageBuffer new_data(sz);
Rescale(ImageWriter(new_data, false), sz, ImageBufferRaster(block.block), block.size);
block.block = new_data;
}
else {
block.Init(SUNIT << count, info.background);
block.level = level + count;
block.area = RectC(px << count, py << count, 1 << count, 1 << count);
drawback(block);
}
}
}
else
{ // too big - bisect to generate higher level
// TIMING("HRR::Write (long step)");
Block *ptr = 0;
if(downscale) {
Size part_size = GetLogBlockSize(GetLogBlockRect(level, RectC(px, py, 1, 1)), info.log_rect);
if(part_size.cx <= 0 || part_size.cy <= 0)
return false;
block.Init(part_size, info.background);
ptr = &block;
}
bool done = Write(drawback, downscale, level + 1, 2 * px + 0, 2 * py + 0, format, ptr);
done |= Write(drawback, downscale, level + 1, 2 * px + 1, 2 * py + 0, format, ptr);
done |= Write(drawback, downscale, level + 1, 2 * px + 0, 2 * py + 1, format, ptr);
done |= Write(drawback, downscale, level + 1, 2 * px + 1, 2 * py + 1, format, ptr);
if(!done && downscale)
return false;
if(!downscale) {
block.Init(SUNIT, info.background);
block.level = level;
block.area = RectC(px, py, 1, 1);
block.log_area = GetLogBlockRect(block.level, block.area);
drawback(block);
}
int lin = px + (py << level);
// TIMING("HRR::Write / save (indirect)");
if(info.mono || IsNull(info.background)) {
int kind = GetMaskInfo(block.block, block.block.GetLength());
if(kind && !info.mono) {
int pixoff = CeilPack64(stream.GetPos());
pixel_directory[level][lin] = pixoff;
int64 pixpos = Unpack64(pixoff);
if(stream.GetSize() < pixpos)
stream.Put(0, (int)(pixpos - stream.GetSize()));
stream.Seek(pixpos);
//Stream64Stream pixstream(stream, pixpos);
format.Save(stream, ImageBufferRaster(block.block));
}
if(kind == 2 || (kind == 1 && info.mono)) {
String s = EncodeMask(block.block, version >= 5);
ASSERT(s.GetLength() >= 4);
int maskoff = CeilPack64(stream.GetPos());
mask_directory[level][lin] = maskoff;
int64 maskpos = Unpack64(maskoff);
if(stream.GetSize() < maskpos)
stream.Put(0, (int)(maskpos - stream.GetSize()));
stream.Seek(maskpos);
stream.PutIL(s.GetLength());
stream.Put(s, s.GetLength());
}
}
else
{
int pixoff = CeilPack64(stream.GetPos());
pixel_directory[level][lin] = pixoff;
int64 pixpos = Unpack64(pixoff);
while(stream.GetSize() < pixpos)
stream.Put(0, (int)min<int64>(pixpos - stream.GetSize(), 1 << 24));
stream.Seek(pixpos);
//Stream64Stream pixstream(stream, pixpos);
format.Save(stream, ImageBufferRaster(block.block));
}
}
if(put) {
// TIMING("HRR::Write / put");
Rect org = RectC((px & 1) << info.HALF_BITS, (py & 1) << info.HALF_BITS,
1 << info.HALF_BITS, 1 << info.HALF_BITS);
Rescale(ImageWriter(put->block, org.TopLeft(), false), org.Size(), ImageBufferRaster(block.block), RUNIT);
}
return true;
}
void HRR::SetMap(String key, String value)
{
if(IsNull(value))
{
int i = map.Find(key);
if(i >= 0)
map.Remove(i);
}
else
map.GetAdd(key) = value;
}
void HRR::FlushMap()
{
ASSERT(stream.IsOpen());
if(map_offset == 0)
map_offset = CeilPack64(stream.GetSize());
stream.Seek(0);
stream.SetStoring();
Serialize();
}
int HRR::SizeOfInstance() const
{
return sizeof(*this) + directory_sizeof + cache_sizeof;
}
END_UPP_NAMESPACE