360 lines
9.1 KiB
JavaScript
360 lines
9.1 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2024
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
(function(window, undefined) {
|
|
|
|
function CRasterHeapLineFree()
|
|
{
|
|
this.Y = 0;
|
|
this.Height = 0;
|
|
}
|
|
|
|
function CRasterDataInfo()
|
|
{
|
|
this.Chunk = null;
|
|
this.Line = null;
|
|
this.Index = 0;
|
|
}
|
|
|
|
function CRasterHeapLine()
|
|
{
|
|
this.Y = 0;
|
|
this.Height = 0;
|
|
this.Count = 0;
|
|
this.CountBusy = 0;
|
|
this.Images = null;
|
|
this.Index = 0;
|
|
}
|
|
CRasterHeapLine.prototype.CreatePlaces = function(width, height, widthLine)
|
|
{
|
|
this.Height = height;
|
|
this.Count = (widthLine / width) >> 0;
|
|
|
|
var _size = this.Count;
|
|
var arr = null;
|
|
if (typeof(Int8Array) != 'undefined' && !window.opera)
|
|
arr = new Int8Array(_size);
|
|
else
|
|
arr = new Array(_size);
|
|
|
|
for (var i=0;i<_size;i++)
|
|
arr[i] = 0;
|
|
|
|
this.Images = arr;
|
|
};
|
|
|
|
CRasterHeapLine.prototype.Alloc = function()
|
|
{
|
|
if (this.Count == this.CountBusy)
|
|
return -1;
|
|
|
|
var arr = this.Images;
|
|
if (arr[this.CountBusy] == 0)
|
|
{
|
|
arr[this.CountBusy] = 1;
|
|
this.CountBusy += 1;
|
|
return this.CountBusy - 1;
|
|
}
|
|
|
|
var _len = this.Count;
|
|
for (var i = 0; i < _len; i++)
|
|
{
|
|
if (arr[i] == 0)
|
|
{
|
|
arr[i] = 1;
|
|
this.CountBusy += 1;
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
CRasterHeapLine.prototype.Free = function(index)
|
|
{
|
|
if (this.Images[index] == 1)
|
|
{
|
|
this.Images[index] = 0;
|
|
this.CountBusy -= 1;
|
|
}
|
|
return this.CountBusy;
|
|
};
|
|
|
|
function CRasterHeapChuck()
|
|
{
|
|
this.CanvasImage = null;
|
|
this.CanvasCtx = null;
|
|
|
|
this.Width = 0;
|
|
this.Height = 0;
|
|
|
|
this.LinesFree = [];
|
|
this.LinesBusy = [];
|
|
|
|
this.CurLine = null;
|
|
|
|
this.FindOnlyEqualHeight = false;
|
|
}
|
|
CRasterHeapChuck.prototype.Create = function(width, height)
|
|
{
|
|
this.Width = width;
|
|
this.Height = height;
|
|
|
|
this.CanvasImage = document.createElement('canvas');
|
|
this.CanvasImage.width = width;
|
|
this.CanvasImage.height = height;
|
|
|
|
this.CanvasCtx = this.CanvasImage.getContext('2d');
|
|
this.CanvasCtx.globalCompositeOperation = "source-atop";
|
|
|
|
var _freeLine = new CRasterHeapLineFree();
|
|
_freeLine.Y = 0;
|
|
_freeLine.Height = this.Height;
|
|
this.LinesFree[0] = _freeLine;
|
|
};
|
|
CRasterHeapChuck.prototype.Clear = function()
|
|
{
|
|
this.LinesBusy.splice(0, this.LinesBusy.length);
|
|
this.LinesFree.splice(0, this.LinesFree.length);
|
|
|
|
var _freeLine = new CRasterHeapLineFree();
|
|
_freeLine.Y = 0;
|
|
_freeLine.Height = this.Height;
|
|
this.LinesFree[0] = _freeLine;
|
|
};
|
|
CRasterHeapChuck.prototype.Alloc = function(width, height)
|
|
{
|
|
var _need_height = Math.max(width, height);
|
|
var _busy_len = this.LinesBusy.length;
|
|
for (var i = 0; i < _busy_len; i++)
|
|
{
|
|
var _line = this.LinesBusy[i];
|
|
if (_line.Height >= _need_height)
|
|
{
|
|
var _index = _line.Alloc();
|
|
if (-1 != _index)
|
|
{
|
|
var _ret = new CRasterDataInfo();
|
|
_ret.Chunk = this;
|
|
_ret.Line = _line;
|
|
_ret.Index = _index;
|
|
return _ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
// линию не нашли. Начинаем искать из свободной памяти
|
|
// ищем 3/2 от нужного размера. и параллельно 1
|
|
var _need_height1 = (3 * _need_height) >> 1;
|
|
if (this.FindOnlyEqualHeight)
|
|
_need_height1 = _need_height;
|
|
|
|
var _free_len = this.LinesFree.length;
|
|
var _index_found_koef1 = -1;
|
|
for (var i = 0; i < _free_len; i++)
|
|
{
|
|
var _line = this.LinesFree[i];
|
|
if (_line.Height >= _need_height1)
|
|
{
|
|
// нашли
|
|
var _new_line = new CRasterHeapLine();
|
|
_new_line.CreatePlaces(_need_height1, _need_height1, this.Width);
|
|
_new_line.Y = _line.Y;
|
|
_new_line.Index = this.LinesBusy.length;
|
|
this.LinesBusy.push(_new_line);
|
|
|
|
_line.Y += _need_height1;
|
|
_line.Height -= _need_height1;
|
|
|
|
if (_line.Height == 0)
|
|
this.LinesFree.splice(i, 1);
|
|
|
|
var _ret = new CRasterDataInfo();
|
|
_ret.Chunk = this;
|
|
_ret.Line = _new_line;
|
|
_ret.Index = _new_line.Alloc();
|
|
return _ret;
|
|
}
|
|
else if (_line.Height >= _need_height && -1 == _index_found_koef1)
|
|
{
|
|
_index_found_koef1 = i;
|
|
}
|
|
}
|
|
|
|
// 3/2 не нашли. если нашли для 1, то выделяем там
|
|
if (-1 != _index_found_koef1)
|
|
{
|
|
var _line = this.LinesFree[_index_found_koef1];
|
|
|
|
var _new_line = new CRasterHeapLine();
|
|
_new_line.CreatePlaces(_need_height, _need_height, this.Width);
|
|
_new_line.Y = _line.Y;
|
|
_new_line.Index = this.LinesBusy.length;
|
|
this.LinesBusy.push(_new_line);
|
|
|
|
_line.Y += _need_height;
|
|
_line.Height -= _need_height;
|
|
|
|
if (_line.Height == 0)
|
|
this.LinesFree.splice(i, 1);
|
|
|
|
var _ret = new CRasterDataInfo();
|
|
_ret.Chunk = this;
|
|
_ret.Line = _new_line;
|
|
_ret.Index = _new_line.Alloc();
|
|
return _ret;
|
|
}
|
|
|
|
// не нашли.
|
|
return null;
|
|
};
|
|
CRasterHeapChuck.prototype.Free = function(obj)
|
|
{
|
|
var _refs = obj.Line.Free(obj.Index);
|
|
if (_refs == 0)
|
|
{
|
|
// нужно удалить линию и перебить всем оставшимся индексы
|
|
|
|
var _line = obj.Line;
|
|
this.LinesBusy.splice(_line.Index, 1);
|
|
|
|
var _lines_busy = this.LinesBusy;
|
|
var _busy_len = _lines_busy.length;
|
|
for (var i = _line.Index; i < _busy_len; i++)
|
|
_lines_busy[i].Index = i;
|
|
|
|
// теперь нужно поправить linesfree
|
|
var y1 = _line.Y;
|
|
var y2 = _line.Y + _line.Height;
|
|
var _lines_free = this.LinesFree;
|
|
var _free_len = _lines_free.length;
|
|
|
|
var _ind_prev = -1;
|
|
var _ind_next = -1;
|
|
for (var i = 0; i < _free_len; i++)
|
|
{
|
|
var _line_f = _lines_free[i];
|
|
if (-1 == _ind_prev)
|
|
{
|
|
if (y1 == (_line_f.Y + _line_f.Height))
|
|
_ind_prev = i;
|
|
}
|
|
else if (-1 == _ind_next)
|
|
{
|
|
if (y2 == _line_f.Y)
|
|
_ind_next = i;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// нашли прилегаюую свободную память. теперь нужно их склеить, или создать новую
|
|
if (-1 != _ind_prev && -1 != _ind_next)
|
|
{
|
|
_lines_free[_ind_prev].Height += (_line.Height + _lines_free[_ind_next].Height);
|
|
_lines_free.splice(_ind_next, 1);
|
|
}
|
|
else if (-1 != _ind_prev)
|
|
{
|
|
_lines_free[_ind_prev].Height += _line.Height;
|
|
}
|
|
else if (-1 != _ind_next)
|
|
{
|
|
_lines_free[_ind_next].Y -= _line.Height;
|
|
_lines_free[_ind_next].Height += _line.Height;
|
|
}
|
|
else
|
|
{
|
|
var _new_line = new CRasterHeapLineFree();
|
|
_new_line.Y = _line.Y;
|
|
_new_line.Height = _line.Height;
|
|
_lines_free.push(_new_line);
|
|
}
|
|
_line = null;
|
|
}
|
|
};
|
|
|
|
function CRasterHeapTotal(_size)
|
|
{
|
|
this.ChunkHeapSize = (undefined === _size) ? 3000 : _size; // 4 * 3000 * 3000 = 36Mb
|
|
this.Chunks = [];
|
|
}
|
|
CRasterHeapTotal.prototype.Clear = function()
|
|
{
|
|
var _len = this.Chunks.length;
|
|
for (var i = 0; i < _len; i++)
|
|
{
|
|
this.Chunks[i].Clear();
|
|
}
|
|
|
|
// теперь наверное удалим и память лишнюю
|
|
if (_len > 1)
|
|
this.Chunks.splice(1, _len - 1);
|
|
};
|
|
|
|
CRasterHeapTotal.prototype.Alloc = function(width, height)
|
|
{
|
|
var _len = this.Chunks.length;
|
|
for (var i = 0; i < _len; i++)
|
|
{
|
|
var _ret = this.Chunks[i].Alloc(width, height);
|
|
if (null != _ret)
|
|
return _ret;
|
|
}
|
|
this.HeapAlloc(this.ChunkHeapSize, this.ChunkHeapSize);
|
|
return this.Chunks[_len].Alloc(width, height);
|
|
};
|
|
|
|
CRasterHeapTotal.prototype.HeapAlloc = function(width, height)
|
|
{
|
|
var _chunk = new CRasterHeapChuck();
|
|
_chunk.Create(width, height);
|
|
this.Chunks[this.Chunks.length] = _chunk;
|
|
};
|
|
|
|
CRasterHeapTotal.prototype.CreateFirstChuck = function(_w, _h)
|
|
{
|
|
if (0 == this.Chunks.length)
|
|
{
|
|
this.Chunks[0] = new CRasterHeapChuck();
|
|
this.Chunks[0].Create((undefined == _w) ? this.ChunkHeapSize : _w, (undefined == _h) ? this.ChunkHeapSize : _h);
|
|
}
|
|
};
|
|
|
|
window['AscFonts'] = window['AscFonts'] || {};
|
|
window['AscFonts'].CRasterHeapTotal = CRasterHeapTotal;
|
|
|
|
})(window, undefined);
|