powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Развернуть содержимое Image на 90гр.
11 сообщений из 11, страница 1 из 1
Развернуть содержимое Image на 90гр.
    #32223512
Арнис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Народ! Наверное я уже всех достал :) , но как проделать Subj?
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32223541
Фотография Mihail R.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
самостоятельно попиксельно прорисовать.
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32223544
Фотография Mihail R.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
создать имадж развернутый, и заменить его.
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32223569
Арнис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Честно говоря - ничего не понял. Мне-бы примерчик :)
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32223867
Дмитрий Мыльников
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Э-хе-хе...
Как, как, берёшь и поворачиваешь. :)

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
porcedure RotateBitmap(Bitmap:TBitmap);
var i,j,ei,ej:integer;
     Buffer:TBitmap;
begin
  Buffer:=TBitmap.Create;
  try
    Buffer.HandleType:=Bitmap.HandleType;
    Buffer.PixelFormat:=Bitmap.PixelFormat;
    Buffer.Palette:=Bitmap.Palette;
    Buffer.Width:=Bitmap.Height;
    Buffer.Height:=Bitmap.Width;
    ei:=Bitmap.Height- 1 ; ej:=Bitmap.Width- 1 ;
    for i:= 0  to ei do
      for j:= 0  to ej do 
        Buffer.Canvas.Pixels[i,ej-j]:=Bitmap.Canvas.Pixels[j,i];
//в зависимости от направления поворота может быть Buffer.Canvas.Pixels[ei-i,j]:=Bitmap.Canvas.Pixels[j,i];
    Bitmap.Assign(Buffer);
  finally Buffer.Free; end;
end;


Это решение "в лоб", поэтому не самое быстрое. Если нужно быстро, то тогда придётся использовать свойство Bitmap.ScanLine, но это уже высший пилотаж и нужно будет рабираться с форматом растров и работать с битами в зависимости от количества цветов.

Кстати, не забывайте про свойство Bitmap.HandleType. Дело в том, что изначально создаётся битмап, у которого задано bmDDB - аппаратно зависимый битмап. Он имеет тоже самое количество цветов, что и текущие настройки дисплея и разворачивается СНИЗУ ВВЕРХ, то есть, хранится как бы вверх ногами в зеркальном отображении. А на диске большинство BMP хранятся как bmDIB - аппаратно независимый битмап (и это правильно), которые хранятся сверху вниз, как и большинство остальных растровых форматов.

Ещё один прикол в том, что в Win9x на bmDDB ограничение на размеры - общее количество точек растра не более 8 миллионов (определено экспериментально), при попытке создать растр DDB большего размера выдаётся сообщение "не хватает ресурсов системы". А вот растры DIB создаются без особых проблем. Не считая наличия необходимой памяти, конечно, которая может быть в том числе виртуальной (в своп-файле на диске).
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32223980
Арнис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Оно конечно всё хорошо. Но мне-бы Jpeg покрутить :( Похожий вариант я нашёл - тока под Jpeg переделать немогу :(
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32224411
Дмитрий Мыльников
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А, вот оно чего. Ну так скопируй его временно в TBitmap, потом обработай, а потом снова в TJPEGImage.
См. пример с изменением размера. Я там именно так и делал. Изображение копируется через вызов Bitmap.Assign(Jpeg); и обратно Jpeg.Assign(Bitmap);
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32224504
Фотография Andrew Campball
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
KULIBA1000.CHM
Поворот изображения на 90 градусов

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
697.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
729.
730.
731.
732.
733.
734.
735.
736.
737.
738.
739.
740.
741.
742.
743.
744.
745.
746.
747.
748.
749.
750.
751.
752.
753.
754.
755.
756.
757.
758.
759.
760.
761.
762.
763.
764.
765.
766.
767.
768.
769.
770.
771.
772.
773.
774.
775.
776.
777.
778.
779.
780.
781.
782.
783.
784.
785.
786.
787.
788.
789.
790.
791.
792.
793.
794.
795.
796.
797.
798.
799.
800.
801.
802.
803.
804.
805.
806.
807.
808.
809.
810.
811.
812.
813.
814.
815.
816.
817.
818.
819.
820.
821.
822.
823.
824.
825.
826.
827.
828.
829.
830.
831.
832.
833.
834.
835.
836.
837.
838.
839.
840.
841.
842.
843.
844.
845.
846.
847.
848.
849.
850.
851.
852.
853.
854.
855.
856.
857.
858.
859.
860.
861.
862.
863.
864.
865.
866.
867.
868.
869.
870.
871.
872.
873.
874.
875.
876.
877.
878.
879.
880.
881.
882.
883.
884.
885.
886.
887.
888.
889.
890.
891.
892.
893.
894.
895.
896.
897.
898.
899.
900.
901.
902.
903.
904.
905.
906.
907.
908.
909.
910.
911.
912.
913.
914.
915.
916.
917.
918.
919.
920.
921.
922.
923.
924.
925.
926.
927.
928.
929.
930.
931.
932.
933.
934.
935.
936.
937.
938.
939.
940.
941.
942.
943.
944.
945.
946.
947.
948.
949.
950.
951.
952.
953.
954.
955.
956.
957.
958.
959.
960.
961.
962.
963.
964.
965.
966.
967.
968.
969.
970.
971.
972.
973.
974.
975.
976.
977.
978.
979.
980.
981.
982.
983.
984.
985.
986.
987.
988.
989.
990.
991.
992.
993.
994.
995.
996.
997.
998.
999.
1000.
1001.
1002.
1003.
1004.
1005.
1006.
1007.
1008.
1009.
1010.
1011.
1012.
1013.
1014.
1015.
1016.
1017.
1018.
1019.
1020.
1021.
1022.
1023.
1024.
1025.
1026.
1027.
1028.
1029.
1030.
1031.
1032.
1033.
1034.
1035.
1036.
1037.
1038.
1039.
Новый модуль имеет три программы: RotateBitmap90DegreesClockwise, RotateBitmap90DegreesCounterClockwise, и RotateBitmap180Degrees. Все три используют TBitmap как переменную и вращают его согласно своему названию.
Два предостережения: Это все еще не совсем работает в Delphi3. Появляется какой-то шум на краях изображения. Мне кажется это из-за какой-то ошибки в методе LoadFromStream объекта TBitmap, но это может быть и моей ошибкой. Тем не менее есть другие решения, связанные с использованием свойства ScanLine, так что эта проблема решается. Во-вторых, этот алгоритм не работает с сжатыми RLE-алгоритмом изображениями.  4 - и  8 -битные (по разрешению) изображения могут быть декодированы и хранится в памяти: на случай, если они потребуются, у нас есть их дескриптор. К тому же, если изображение сжато, можно просто получить дескриптор канвы с нормальным изображением:



 --------------------------------------------------------------------------------
 
    ABitmap.Canvas.Handle;  

Этим мы также назначаем контекст устройства (то есть экрана), и, вероятно, сможем обрабатывать изображения вплоть до  24 -битного формата. Что-то вроде компромисного решения.

Во всяком случае это работает у меня в Delphi  1  и  2  с черно-белыми,  4 -,  8 -,  16 -,  24 -, и  32 -битными изображениями (но не с  4 - и  8 -битными изображениями, сжатыми RLE-алгоритмом, как я уже говорил выше).



 --------------------------------------------------------------------------------
 
    unit bmpRot;

interface

uses

(*$IFDEF Win32*) Windows, (*$ELSE*) WinTypes, WinProcs, (*$ENDIF*)
Classes, Graphics;

procedure RotateBitmap90DegreesCounterClockwise(var ABitmap: TBitmap);
procedure RotateBitmap90DegreesClockwise(var ABitmap: TBitmap);
procedure RotateBitmap180Degrees(var ABitmap: TBitmap);

implementation

uses

Dialogs;


(*$IFNDEF Win32*)

type

DWORD = LongInt;
TSelOfs = record
L, H: Word;
end;

procedure Win16Dec(var P: Pointer; const N: LongInt); forward;

procedure Win16Inc(var P: Pointer; const N: LongInt);
begin

if N <  0  then
Win16Dec(P, -N)
else if N >  0  then begin
Inc( TSelOfs(P).H, TSelOfs(N).H * SelectorInc );
Inc( TSelOfs(P).L, TSelOfs(N).L );
if TSelOfs(P).L < TSelOfs(N).L then Inc( TSelOfs(P).H, SelectorInc );
end;
end;

procedure Win16Dec(var P: Pointer; const N: LongInt);
begin

if N <  0  then
Win16Inc(P, -N)
else if N >  0  then begin
if TSelOfs(N).L > TSelOfs(P).L then Dec( TSelOfs(P).H, SelectorInc );
Dec( TSelOfs(P).L, TSelOfs(N).L );
Dec( TSelOfs(P).H, TSelOfs(N).H * SelectorInc );
end;
end;

(*
procedure HugeShift; far; external 'KERNEL' index  113 ;
procedure Win16Dec(var P: Pointer; const N: LongInt); forward;

procedure Win16Inc(var HugePtr: Pointer; Amount: LongInt);

procedure HugeInc; assembler;
asm
mov ax, Amount.Word[ 0 ]        { Сохраняем сумму в DX:AX. }
mov dx, Amount.Word[ 2 ]
les bx, HugePtr               { Получаем ссылку на HugePtr. }
add ax, es:[bx]               { Добавление коррекции. }
adc dx,  0                      { Распространяем перенос на наибольшую величину суммы. }
mov cx, Offset HugeShift
shl dx,                       { Перемещаем наибольшую величину суммы для сегмента. }
add es:[bx+ 2 ], dx             { Увеличиваем сегмент HugePtr. }
mov es:[bx], ax
end;

begin

if Amount >  0  then HugeInc else if Amount <  0  then Win16Dec(HugePtr, -Amount);
end;

procedure Win16Dec(var P: Pointer; const N: LongInt);
begin

if N <  0  then
Win16Inc(P, -N)
else if N >  0  then begin
if TSelOfs(N).L > TSelOfs(P).L then Dec( TSelOfs(P).H, SelectorInc );
Dec( TSelOfs(P).L, TSelOfs(N).L );
Dec( TSelOfs(P).H, TSelOfs(N).H * SelectorInc );
end;
end;
*)

(*$ENDIF*)

procedure RotateBitmap90DegreesCounterClockwise(var ABitmap: TBitmap);
const

BitsPerByte =  8 ;

var

{
Целая куча переменных. Некоторые имеют дело только с одно- и четырех-битовыми
изображениями, другие только с восемью- и  24 -битовыми, а некоторые с обоими.
Любая переменная, оканчивающаяся символом 'R', имеет отношение к вращению изображения,
например если MemoryStream содержит исходное изображение, то MemoryStreamR - повернутое.
}
PbmpInfoR: PBitmapInfoHeader;
bmpBuffer, bmpBufferR: PByte;
MemoryStream, MemoryStreamR: TMemoryStream;
PbmpBuffer, PbmpBufferR: PByte;
BytesPerPixel, PixelsPerByte: LongInt;
BytesPerScanLine, BytesPerScanLineR: LongInt;
PaddingBytes: LongInt;
BitmapOffset: LongInt;
BitCount: LongInt;
WholeBytes, ExtraPixels: LongInt;
SignificantBytes, SignificantBytesR: LongInt;
ColumnBytes: LongInt;
AtLeastEightBitColor: Boolean;
T: LongInt;

procedure NonIntegralByteRotate; (* вложение *)
{

Эта программа осуществляет поворот изображений с разрешением меньшим, чем  8  бит на пиксел,
а имеено: черно-белые ( 1 -бит) и  16 -цветные ( 4 -бит) изображения. Имейте в виду, что
такие вещи, как  2 -битные изображения также могли бы вращаться, но Microsoft не включил
данный формат в свои спецификации и не поддерживает его.
}
var

X, Y: LongInt;
I: LongInt;
MaskBits, CurrentBits: Byte;
FirstMask, LastMask: Byte;
PFirstScanLine: PByte;
FirstIndex, CurrentBitIndex: LongInt;
ShiftRightAmount, ShiftRightStart: LongInt;

begin

(*$IFDEF Win32*)
Inc(PbmpBuffer, BytesPerScanLine * (PbmpInfoR^.biHeight -  1 ) );
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer), BytesPerScanLine * (PbmpInfoR^.biHeight -  1 ) );
(*$ENDIF*)


{ PFirstScanLine движется вдоль первой линии чередования bmpBufferR. }
PFirstScanLine := bmpBufferR;


{ Устанавливаем индексирование. }
FirstIndex := BitsPerByte - BitCount;


{
Устанавливаем битовые маски:


Для черно-белого изображения,
LastMask  :=  00000001     и
FirstMask :=  10000000 


Для  4 -битного изображения,
LastMask  :=  00001111     и
FirstMask :=  11110000 


Зададим значения CurrentBits и MaskBits, так как мы будем перемещаться по ним:
Для монохромных изображений:
 10000000 ,  01000000 ,  00100000 ,  00010000 ,  00001000 ,  00000100 ,  00000010 ,  00000001 
Для  4 -битных изображений:
 11110000 ,  00001111 


CurrentBitIndex определяет расстояние от крайнего правого бита
до позиции CurrentBits. Например, если мы находимся в одиннадцатой
колонке черно-белого изображения, CurrentBits равен
 11  mod  8  :=  3 , или  3 -й самый левый бит. Таким образом, крайне правый
бит должен переместиться на четыре позиции, чтобы попасть на позицию
CurrentBits. CurrentBitIndex как раз и хранит такое значение.
}
LastMask :=  1  shl BitCount -  1 ;
FirstMask := LastMask shl FirstIndex;


CurrentBits := FirstMask;
CurrentBitIndex := FirstIndex;


ShiftRightStart := BitCount * (PixelsPerByte -  1 );


{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. }
{ Помните что DIB'ы имеют происхождение противоположное DDB'сам. }


{ Счетчик Y указывает на текущую строчку исходного изображения. }
for Y :=  1  to PbmpInfoR^.biHeight do begin
PbmpBufferR := PFirstScanLine;


{
Счетчик X указывает на текущую колонку пикселей исходного изображения.
Здесь мы имеем дело только с полностью заполненными байтами. Обработка
'частично заполненных' байтов происходит ниже.
}
for X :=  1  to WholeBytes do begin
{
Выбираем биты, начиная с  10000000  для черно-белых и
заканчивая  11110000  для  4 -битных изображений.
}
MaskBits := FirstMask;
{
ShiftRightAmount - сумма, необходимая для перемещения текущего байта
через весь путь (помните, я об этом говорил выше) в правую часть.
}
ShiftRightAmount := ShiftRightStart;
for I :=  1  to PixelsPerByte do begin
{
Вот гарнир. Берем текущий байт вращаемого изображения и маскируем его
с not CurrentBits. Гасятся нулями только биты CurrentBits, сам байт перемещается
без изменений. Пример: Для черно-белого изображения, если бы мы
находились в  11 -й колонке (см. пример выше), мы должны нулем погасить
 3 -й слева бит, то есть мы должны использовать PbmpBufferR^ и  11011111 .


Теперь рассмотрим наш текущий исходный байт. Для черно-белых изображений
мы организуем цикл с шагом через бит, в общей сложности для восьми пикселей.
Для  4 -битных изображений мы делаем цикл с обработкой четырех битов за проход для
двух пикселей. В любом случае мы делаем это через маскирование с
MaskBits ('PbmpBuffer^ и MaskBits'). Теперь нам нужно получить бит(ы)
из той колонки(ок), на которую отобразится CurrentBits. Мы это делаем с помощью
перемещения их в крайне правую часть байта ('shr ShiftRightAmount'),
затем сдвигая их налево с помощью вышеупомянутого
CurrentBitIndex ('shl CurrentBitIndex'). Дело в том, что хотя перемещение
вправо с параметром -n должно быть просто перемещением налево с параметром +n,
в Delphi это не работает. Итак, мы начинаем с первого байта, перемещая пиксели
в правую часть насколько это возможно незанятыми позициями.


Наконец, мы имеем наш исходный бит(ы), перемещенный на нужное место
с погашенными нулями битами. Последнее делаем непосредственно или с
помощью PbmpBufferR^ (гасим биты в CurrentBits, помните?).


Мда...  "Просто" . Ладно, поехали дальше.
}


PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or
( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );


{ Сдвигаем MaskBits для следующей итерации. }
MaskBits := MaskBits shr BitCount;
(*$IFDEF Win32*)
{ Перемещаем наш указатель на буфер вращаемого изображения на одну линию чередования. }
Inc(PbmpBufferR, BytesPerScanLineR);
{ Нам не нужно перемещаться непосредственно вправо в течение некоторого времени. }
Dec(ShiftRightAmount, BitCount);
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR );
Win16Dec( Pointer(ShiftRightAmount), BitCount );
(*$ENDIF*)
end;
(*$IFDEF Win32*)
Inc(PbmpBuffer);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer),  1  );
(*$ENDIF*)
end;


{ Если есть  "частично заполненный"  байт, самое время о нем позаботиться. }
if ExtraPixels <>  0  then begin
{ Делаем такие же манипуляции, как в цикле выше. }
MaskBits := FirstMask;
ShiftRightAmount := ShiftRightStart;
for I :=  1  to ExtraPixels do begin
PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or
( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );


MaskBits := MaskBits shr BitCount;
(*$IFDEF Win32*)
Inc(PbmpBufferR, BytesPerScanLineR);
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR );
(*$ENDIF*)
Dec(ShiftRightAmount, BitCount);
end;
(*$IFDEF Win32*)
Inc(PbmpBuffer);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer),  1  );
(*$ENDIF*)
end;


(*$IFDEF Win32*)
{ Пропускаем заполнение. }
Inc(PbmpBuffer, PaddingBytes);
{
Сохраняем только что просмотренную линию чередования и переходим к следующей
для получения набора очередной строки.
}
Dec(PbmpBuffer, BytesPerScanLine shl  1 );
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer), PaddingBytes );
Win16Dec( Pointer(PbmpBuffer), BytesPerScanLine shl  1  );
(*$ENDIF*)


if CurrentBits = LastMask then begin
{ Мы в конце этого байта. Начинаем с другой колонки. }
CurrentBits := FirstMask;
CurrentBitIndex := FirstIndex;
{ Идем вниз колонки вращаемого изображения , но одну колонку пропускаем. }
(*$IFDEF Win32*)
Inc(PFirstScanLine);
(*$ELSE*)
Win16Inc( Pointer(PFirstScanLine),  1  );
(*$ENDIF*)
end
else begin
{ Продолжаем заполнять этот байт. }
CurrentBits := CurrentBits shr BitCount;
Dec(CurrentBitIndex, BitCount);
end;
end;
end; { procedure NonIntegralByteRotate (* вложение *) }

procedure IntegralByteRotate; (* вложение *)
var

X, Y: LongInt;
(*$IFNDEF Win32*)
I: Integer;
(*$ENDIF*)

begin

{ Перемещаем PbmpBufferR в последнюю колонку первой линии чередования bmpBufferR. }
(*$IFDEF Win32*)
Inc(PbmpBufferR, SignificantBytesR - BytesPerPixel);
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR), SignificantBytesR - BytesPerPixel );
(*$ENDIF*)


{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. }
{ Помните что DIB'ы имеют происхождение противоположное DDB'сам. }
for Y :=  1  to PbmpInfoR^.biHeight do begin
for X :=  1  to PbmpInfoR^.biWidth do begin
{ Копируем пиксели. }
(*$IFDEF Win32*)
Move(PbmpBuffer^, PbmpBufferR^, BytesPerPixel);
Inc(PbmpBuffer, BytesPerPixel);
Inc(PbmpBufferR, BytesPerScanLineR);
(*$ELSE*)
for I :=  1  to BytesPerPixel do begin
PbmpBufferR^ := PbmpBuffer^;
Win16Inc( Pointer(PbmpBuffer),  1  );
Win16Inc( Pointer(PbmpBufferR),  1  );
end;
Win16Inc( Pointer(PbmpBufferR), BytesPerScanLineR - BytesPerPixel);
(*$ENDIF*)
end;
(*$IFDEF Win32*)
{ Пропускаем заполнение. }
Inc(PbmpBuffer, PaddingBytes);
{ Идем вверх колонки вращаемого изображения , но одну колонку пропускаем. }
Dec(PbmpBufferR, ColumnBytes + BytesPerPixel);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer), PaddingBytes);
Win16Dec( Pointer(PbmpBufferR), ColumnBytes + BytesPerPixel);
(*$ENDIF*)
end;
end;

{ Это тело процедуры RotateBitmap90DegreesCounterClockwise. }
begin

{ Никогда сами не вызывайте GetDIBSizes! Это испортит ваше изображение. }


MemoryStream := TMemoryStream.Create;


{
Для работы: Прежде всего установим размер. Это устранит перераспределение памяти
для MemoryStream. Вызов GetDIBSizes будет к месту, но, как отмечалось выше,
это может исказить ваше изображение. Вызов некоторых API функций вероятно
позаботился бы об этом, но это тема отдельного разговора.
}


{ Недокументированный метод. Все же программист иногда сродни шаману. }
ABitmap.SaveToStream(MemoryStream);


{ Изображение больше не нужно. Создадим новое когда понадобится. }
ABitmap.Free;


bmpBuffer := MemoryStream.Memory;
{ Получаем биты компенсации. Они могут содержать информацию о палитре. }
BitmapOffset := PBitmapFileHeader(bmpBuffer)^.bfOffBits;


{ Устанавливаем PbmpInfoR на указатель информационного заголовка исходного изображения. }
{ Эти заголовки могут немного раздражать, но они необходимы для работы. }
(*$IFDEF Win32*)
Inc( bmpBuffer, SizeOf(TBitmapFileHeader) );
(*$ELSE*)
Win16Inc( Pointer(bmpBuffer), SizeOf(TBitmapFileHeader) );
(*$ENDIF*)
PbmpInfoR := PBitmapInfoHeader(bmpBuffer);


{ Устанавливаем bmpBuffer и PbmpBuffer так, чтобы они указывали на биты оригинального изображения. }
bmpBuffer := MemoryStream.Memory;
(*$IFDEF Win32*)
Inc(bmpBuffer, BitmapOffset);
(*$ELSE*)
Win16Inc( Pointer(bmpBuffer), BitmapOffset );
(*$ENDIF*)
PbmpBuffer := bmpBuffer;


{
Имейте в виду, что нам не нужно беспокоиться о совместимости изображений версии  4  и  3 ,
поскольку области, которые мы используем, а именно  -- biWidth, biHeight, и biBitCount --
 
располагаются на один и тех же местах в обоих структурах. Итак, одной проблемой меньше.
Изображения версии OS/ 2 , между прочим, при этом гнусно рушатся. Обидно.
}
with PbmpInfoR^ do begin
{ ShowMessage('Компрессия := ' + IntToStr(biCompression)); }
BitCount := biBitCount;
{ ShowMessage('BitCount := ' + IntToStr(BitCount)); }


{ ScanLines -  "выровненный"  DWORD. }
BytesPerScanLine := ((((biWidth * BitCount) +  31 ) div  32 ) * SizeOf(DWORD));
BytesPerScanLineR := ((((biHeight * BitCount) +  31 ) div  32 ) * SizeOf(DWORD));


AtLeastEightBitColor := BitCount >= BitsPerByte;
if AtLeastEightBitColor then begin
{ Нас не должен волновать бит-тильда. Классно. }
BytesPerPixel := biBitCount shr  3 ;
SignificantBytes := biWidth * BitCount shr  3 ;
SignificantBytesR := biHeight * BitCount shr  3 ;
{ Дополнительные байты необходимы для выравнивания DWORD. }
PaddingBytes := BytesPerScanLine - SignificantBytes;
ColumnBytes := BytesPerScanLineR * biWidth;
end
else begin
{ Одно- или четырех-битовое изображение. Уфф. }
PixelsPerByte := SizeOf(Byte) * BitsPerByte div BitCount;
{ Все количество байтов полностью заполняется информацией о пикселе. }
WholeBytes := biWidth div PixelsPerByte;
{
Обрабатываем любые дополнительные биты, которые могут частично заполнять байт.
Например, черно-белое изображение, у которого  14  пикселей описываются каждый
соответственно своим байтом, плюс одним дополнительным, у которого на самом
деле используются  6  битов, остальное мусор.
}
ExtraPixels := biWidth mod PixelsPerByte;
{
Все дополнительные байты  -- если имеются -- требуется DWORD-выровнять по
 
линии чередования.
}
PaddingBytes := BytesPerScanLine - WholeBytes;
{
Если есть дополнительные биты (то есть имеется 'дополнительный байт'),
то один из заполненных байтов уже был принят во внимание.
}
if ExtraPixels <>  0  then Dec(PaddingBytes);
end; { if AtLeastEightBitColor then }


{ TMemoryStream, обслуживающий вращаемые биты. }
MemoryStreamR := TMemoryStream.Create;
{
Устанавливаем размер вращаемого изображения. Может отличаться
от исходного из-за выравнивания DWORD.
}
MemoryStreamR.SetSize(BitmapOffset + BytesPerScanLineR * biWidth);
end; { with PbmpInfoR^ do }


{ Копируем заголовки исходного изображения. }
MemoryStream.Seek( 0 , soFromBeginning);
MemoryStreamR.CopyFrom(MemoryStream, BitmapOffset);


{ Вот буфер, который мы будем  "вращать" . }
bmpBufferR := MemoryStreamR.Memory;
{ Пропускаем заголовки, yadda yadda yadda... }
(*$IFDEF Win32*)
Inc(bmpBufferR, BitmapOffset);
(*$ELSE*)
Win16Inc( Pointer(bmpBufferR), BitmapOffset );
(*$ENDIF*)
PbmpBufferR := bmpBufferR;


{ Едем дальше. }
if AtLeastEightBitColor then
IntegralByteRotate
else
NonIntegralByteRotate;


{ Удовлетворяемся исходными битами. }
MemoryStream.Free;


{ Теперь устанавливаем PbmpInfoR, чтобы он указывал на информационный заголовок вращаемого изображения. }
PbmpBufferR := MemoryStreamR.Memory;
(*$IFDEF Win32*)
Inc( PbmpBufferR, SizeOf(TBitmapFileHeader) );
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR), SizeOf(TBitmapFileHeader) );
(*$ENDIF*)
PbmpInfoR := PBitmapInfoHeader(PbmpBufferR);


{ Меняем ширину с высотой в информационном заголовке вращаемого изображения. }
with PbmpInfoR^ do begin
T := biHeight;
biHeight := biWidth;
biWidth := T;
biSizeImage :=  0 ;
end;


ABitmap := TBitmap.Create;


{ Вращение с самого начала. }
MemoryStreamR.Seek( 0 , soFromBeginning);
{ Загружаем это снова в ABitmap. }
ABitmap.LoadFromStream(MemoryStreamR);


MemoryStreamR.Free;
end;

procedure RotateBitmap90DegreesClockwise(var ABitmap: TBitmap);
const

BitsPerByte =  8 ;

var

{
Целая куча переменных. Некоторые имеют дело только с одно- и четырех-битовыми
изображениями, другие только с восемью- и  24 -битовыми, а некоторые с обоими.
Любая переменная, оканчивающаяся символом 'R', имеет отношение к вращению изображения,
например если MemoryStream содержит исходное изображение, то MemoryStreamR - повернутое.
}
PbmpInfoR: PBitmapInfoHeader;
bmpBuffer, bmpBufferR: PByte;
MemoryStream, MemoryStreamR: TMemoryStream;
PbmpBuffer, PbmpBufferR: PByte;
BytesPerPixel, PixelsPerByte: LongInt;
BytesPerScanLine, BytesPerScanLineR: LongInt;
PaddingBytes: LongInt;
BitmapOffset: LongInt;
BitCount: LongInt;
WholeBytes, ExtraPixels: LongInt;
SignificantBytes: LongInt;
ColumnBytes: LongInt;
AtLeastEightBitColor: Boolean;
T: LongInt;

procedure NonIntegralByteRotate; (* вложение *)
{

Эта программа осуществляет поворот изображений с разрешением меньшим, чем  8  бит на пиксел,
а имеено: черно-белые ( 1 -бит) и  16 -цветные ( 4 -бит) изображения. Имейте в виду, что
такие вещи, как  2 -битные изображения также могли бы вращаться, но Microsoft не включил
данный формат в свои спецификации и не поддерживает его.
}
var

X, Y: LongInt;
I: LongInt;
MaskBits, CurrentBits: Byte;
FirstMask, LastMask: Byte;
PLastScanLine: PByte;
FirstIndex, CurrentBitIndex: LongInt;
ShiftRightAmount, ShiftRightStart: LongInt;

begin 
{ Перемещаем PLastScanLine в первую колонку последней линии чередования bmpBufferR. }
PLastScanLine := bmpBufferR; (*$IFDEF Win32*) Inc(PLastScanLine, BytesPerScanLineR *
(PbmpInfoR^.biWidth -  1 ) ); (*$ELSE*) Win16Inc( Pointer(PLastScanLine),
BytesPerScanLineR * (PbmpInfoR^.biWidth -  1 ) ); (*$ENDIF*)


{ Устанавливаем индексирование. }
FirstIndex := BitsPerByte - BitCount;


{
Устанавливаем битовые маски:


Для черно-белого изображения,
LastMask  :=  00000001     и
FirstMask :=  10000000 


Для  4 -битного изображения,
LastMask  :=  00001111     и
FirstMask :=  11110000 


Зададим значения CurrentBits и MaskBits, так как мы будем перемещаться по ним:
Для черно-белых изображений:
 10000000 ,  01000000 ,  00100000 ,  00010000 ,  00001000 ,  00000100 ,  00000010 ,  00000001 
Для  4 -битных изображений:
 11110000 ,  00001111 


CurrentBitIndex определяет расстояние от крайнего правого бита
до позиции CurrentBits. Например, если мы находимся в одиннадцатой
колонке черно-белого изображения, CurrentBits равен
 11  mod  8  :=  3 , или  3 -й самый левый бит. Таким образом, крайне правый
бит должен переместиться на четыре позиции, чтобы попасть на позицию
CurrentBits. CurrentBitIndex как раз и хранит такое значение.
}
LastMask :=  1  shl BitCount -  1 ;
FirstMask := LastMask shl FirstIndex;


CurrentBits := FirstMask;
CurrentBitIndex := FirstIndex;


ShiftRightStart := BitCount * (PixelsPerByte -  1 );


{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. }
{ Помните что DIB'ы имеют происхождение противоположное DDB'сам. }


{ Счетчик Y указывает на текущую строчку исходного изображения. }
for Y :=  1  to PbmpInfoR^.biHeight do begin
PbmpBufferR := PLastScanLine;


{
Счетчик X указывает на текущую колонку пикселей исходного изображения.
Здесь мы имеем дело только с полностью заполненными байтами. Обработка
'частично заполненных' байтов происходит ниже.
}
for X :=  1  to WholeBytes do begin
{
Выбираем биты, начиная с  10000000  для черно-белых и
заканчивая  11110000  для  4 -битных изображений.
}
MaskBits := FirstMask;
{
ShiftRightAmount - сумма, необходимая для перемещения текущего байта
через весь путь (помните, я об этом говорил выше) в правую часть.
}
ShiftRightAmount := ShiftRightStart;
for I :=  1  to PixelsPerByte do begin
{
Вот гарнир. Берем текущий байт вращаемого изображения и маскируем его
с not CurrentBits. Гасятся нулями только биты CurrentBits, сам байт перемещается
без изменений. Пример: Для черно-белого изображения, если бы мы
находились в  11 -й колонке (см. пример выше), мы должны нулем погасить
 3 -й слева бит, то есть мы должны использовать PbmpBufferR^ и  11011111 .


Теперь рассмотрим наш текущий исходный байт. Для черно-белых изображений
мы организуем цикл с шагом через бит, в общей сложности для восьми пикселей.
Для  4 -битных изображений мы делаем цикл с обработкой четырех битов за проход для
двух пикселей. В любом случае мы делаем это через маскирование с
MaskBits ('PbmpBuffer^ и MaskBits'). Теперь нам нужно получить бит(ы)
из той колонки(ок), на которую отобразится CurrentBits. Мы это делаем с помощью
перемещения их в крайне правую часть байта ('shr ShiftRightAmount'),
затем сдвигая их налево с помощью вышеупомянутого
CurrentBitIndex ('shl CurrentBitIndex'). Дело в том, что хотя перемещение
вправо с параметром -n должно быть просто перемещением налево с параметром +n,
в Delphi это не работает. Итак, мы начинаем с первого байта, перемещая пиксели
в правую часть насколько это возможно незанятыми позициями.


Наконец, мы имеем наш исходный бит(ы), перемещенный на нужное место
с погашенными нулями битами. Последнее делаем непосредственно или с
помощью PbmpBufferR^ (гасим биты в CurrentBits, помните?).


Мда...  "Просто" . Ладно, поехали дальше.
}


PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or
( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );


{ Сдвигаем MaskBits для следующей итерации. }
MaskBits := MaskBits shr BitCount;
(*$IFDEF Win32*)
{ Перемещаем наш указатель на буфер вращаемого изображения на одну линию чередования. }
Dec(PbmpBufferR, BytesPerScanLineR);
(*$ELSE*)
Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR );
(*$ENDIF*)
{ Нам не нужно перемещаться непосредственно вправо в течение некоторого времени. }
Dec(ShiftRightAmount, BitCount);
end;
(*$IFDEF Win32*)
Inc(PbmpBuffer);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer),  1  );
(*$ENDIF*)
end;


{ Если есть  "частично заполненный"  байт, самое время о нем позаботиться. }
if ExtraPixels <>  0  then begin
{ Делаем такие же манипуляции, как в цикле выше. }
MaskBits := FirstMask;
ShiftRightAmount := ShiftRightStart;
for I :=  1  to ExtraPixels do begin
PbmpBufferR^ := ( PbmpBufferR^ and not CurrentBits ) or
( (PbmpBuffer^ and MaskBits) shr ShiftRightAmount shl CurrentBitIndex );


MaskBits := MaskBits shr BitCount;
(*$IFDEF Win32*)
Dec(PbmpBufferR, BytesPerScanLineR);
(*$ELSE*)
Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR );
(*$ENDIF*)
Dec(ShiftRightAmount, BitCount);
end;
(*$IFDEF Win32*)
Inc(PbmpBuffer);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer),  1  );
(*$ENDIF*)
end;


{ Пропускаем заполнение. }
(*$IFDEF Win32*)
Inc(PbmpBuffer, PaddingBytes);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer), PaddingBytes );
(*$ENDIF*)


if CurrentBits = LastMask then begin
{ Мы в конце этого байта. Начинаем с другой колонки. }
CurrentBits := FirstMask;
CurrentBitIndex := FirstIndex;
{ Идем вниз колонки вращаемого изображения , но одну колонку пропускаем. }
(*$IFDEF Win32*)
Inc(PLastScanLine);
(*$ELSE*)
Win16Inc( Pointer(PLastScanLine),  1  );
(*$ENDIF*)
end
else begin
{ Продолжаем заполнять этот байт. }
CurrentBits := CurrentBits shr BitCount;
Dec(CurrentBitIndex, BitCount);
end;
end;
end; { procedure NonIntegralByteRotate (* вложение *) }

procedure IntegralByteRotate; (* вложение *)
var

X, Y: LongInt;
(*$IFNDEF Win32*)
I: Integer;
(*$ENDIF*)

begin 
{ Перемещаем PbmpBufferR в первую колонку последней линии чередования bmpBufferR. }
(*$IFDEF Win32*)
Inc( PbmpBufferR, BytesPerScanLineR * (PbmpInfoR^.biWidth -  1 ) );
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR) , BytesPerScanLineR * (PbmpInfoR^.biWidth -  1 ) );
(*$ENDIF*)


{ Вот мясо. Перебираем в цикле все пиксели и соответственно вращаем. }
{ Remember that DIBs have their origins opposite from DDBs. }
for Y :=  1  to PbmpInfoR^.biHeight do begin
for X :=  1  to PbmpInfoR^.biWidth do begin
{ Копируем пиксели. }
(*$IFDEF Win32*)
Move(PbmpBuffer^, PbmpBufferR^, BytesPerPixel);
Inc(PbmpBuffer, BytesPerPixel);
Dec(PbmpBufferR, BytesPerScanLineR);
(*$ELSE*)
for I :=  1  to BytesPerPixel do begin
PbmpBufferR^ := PbmpBuffer^;
Win16Inc( Pointer(PbmpBuffer),  1  );
Win16Inc( Pointer(PbmpBufferR),  1  );
end;
Win16Dec( Pointer(PbmpBufferR), BytesPerScanLineR + BytesPerPixel);
(*$ENDIF*)
end;
(*$IFDEF Win32*)
{ Пропускаем заполнение. }
Inc(PbmpBuffer, PaddingBytes);
{ Идем вверх колонки вращаемого изображения , но одну колонку пропускаем. }
Inc(PbmpBufferR, ColumnBytes + BytesPerPixel);
(*$ELSE*)
Win16Inc( Pointer(PbmpBuffer), PaddingBytes );
Win16Inc( Pointer(PbmpBufferR), ColumnBytes + BytesPerPixel );
(*$ENDIF*)
end;
end;

{ Это тело процедуры RotateBitmap90DegreesCounterClockwise. }
begin

{ Никогда сами не вызывайте GetDIBSizes! Это испортит ваше изображение. }


MemoryStream := TMemoryStream.Create;


{
Для работы: Прежде всего установим размер. Это устранит перераспределение памяти
для MemoryStream. Вызов GetDIBSizes будет к месту, но, как отмечалось выше,
это может исказить ваше изображение. Вызов некоторых API функций вероятно
позаботился бы об этом, но это тема отдельного разговора.
}


{ Недокументированный метод. Все же программист иногда сродни шаману. }
ABitmap.SaveToStream(MemoryStream);


{ Don't need you anymore. We'll make a new one when the time comes. }
ABitmap.Free;


bmpBuffer := MemoryStream.Memory;
{ Get the offset bits. This may or may not include palette information. }
BitmapOffset := PBitmapFileHeader(bmpBuffer)^.bfOffBits;


{ Устанавливаем PbmpInfoR на указатель информационного заголовка исходного изображения. }
{ Эти заголовки могут немного раздражать, но они необходимы для работы. }
(*$IFDEF Win32*)
Inc( bmpBuffer, SizeOf(TBitmapFileHeader) );
(*$ELSE*)
Win16Inc( Pointer(bmpBuffer), SizeOf(TBitmapFileHeader) );
(*$ENDIF*)
PbmpInfoR := PBitmapInfoHeader(bmpBuffer);


{ Устанавливаем bmpBuffer и PbmpBuffer так, чтобы они указывали на биты оригинального изображения. }
bmpBuffer := MemoryStream.Memory;
(*$IFDEF Win32*)
Inc(bmpBuffer, BitmapOffset);
(*$ELSE*)
Win16Inc( Pointer(bmpBuffer), BitmapOffset );
(*$ENDIF*)
PbmpBuffer := bmpBuffer;


{
Имейте в виду, что нам не нужно беспокоиться о совместимости изображений версии  4  и  3 ,
поскольку области, которые мы используем, а именно  -- biWidth, biHeight, и biBitCount --
 
располагаются на один и тех же местах в обоих структурах. Итак, одной проблемой меньше.
Изображения версии OS/ 2 , между прочим, при этом гнусно рушатся. Обидно.
}
with PbmpInfoR^ do begin
{ ShowMessage('Компрессия := ' + IntToStr(biCompression)); }
BitCount := biBitCount;
{ ShowMessage('BitCount := ' + IntToStr(BitCount)); }


{ ScanLines -  "выровненный"  DWORD. }
BytesPerScanLine := ((((biWidth * BitCount) +  31 ) div  32 ) * SizeOf(DWORD));
BytesPerScanLineR := ((((biHeight * BitCount) +  31 ) div  32 ) * SizeOf(DWORD));


AtLeastEightBitColor := BitCount >= BitsPerByte;
if AtLeastEightBitColor then begin
{ Нас не должен волновать бит-тильда. Классно. }
BytesPerPixel := biBitCount shr  3 ;
SignificantBytes := biWidth * BitCount shr  3 ;
{ Дополнительные байты необходимы для выравнивания DWORD. }
PaddingBytes := BytesPerScanLine - SignificantBytes;
ColumnBytes := BytesPerScanLineR * biWidth;
end
else begin
{ Одно- или четырех-битовое изображение. Уфф. }
PixelsPerByte := SizeOf(Byte) * BitsPerByte div BitCount;
{ Все количество байтов полностью заполняется информацией о пикселе. }
WholeBytes := biWidth div PixelsPerByte;
{
Обрабатываем любые дополнительные биты, которые могут частично заполнять байт.
Например, черно-белое изображение, у которого  14  пикселей описываются каждый
соответственно своим байтом, плюс одним дополнительным, у которого на самом
деле используются  6  битов, остальное мусор.
}
ExtraPixels := biWidth mod PixelsPerByte;
{
Все дополнительные байты  -- если имеются -- требуется DWORD-выровнять по
 
линии чередования.
}
PaddingBytes := BytesPerScanLine - WholeBytes;
{
Если есть дополнительные биты (то есть имеется 'дополнительный байт'),
то один из заполненных байтов уже был принят во внимание.
}
if ExtraPixels <>  0  then Dec(PaddingBytes);
end; { if AtLeastEightBitColor then }


{ TMemoryStream, обслуживающий вращаемые биты. }
MemoryStreamR := TMemoryStream.Create;
{
Устанавливаем размер вращаемого изображения. Может отличаться
от исходного из-за выравнивания DWORD.
}
MemoryStreamR.SetSize(BitmapOffset + BytesPerScanLineR * biWidth);
end; { with PbmpInfoR^ do }


{ Копируем заголовки исходного изображения. }
MemoryStream.Seek( 0 , soFromBeginning);
MemoryStreamR.CopyFrom(MemoryStream, BitmapOffset);


{ Вот буфер, который мы будем  "вращать" . }
bmpBufferR := MemoryStreamR.Memory;
{ Пропускаем заголовки, yadda yadda yadda... }
(*$IFDEF Win32*)
Inc(bmpBufferR, BitmapOffset);
(*$ELSE*)
Win16Inc( Pointer(bmpBufferR), BitmapOffset );
(*$ENDIF*)
PbmpBufferR := bmpBufferR;


{ Едем дальше. }
if AtLeastEightBitColor then
IntegralByteRotate
else
NonIntegralByteRotate;


{ Удовлетворяемся исходными битами. }
MemoryStream.Free;


{ Теперь устанавливаем PbmpInfoR, чтобы он указывал на информационный заголовок вращаемого изображения. }
PbmpBufferR := MemoryStreamR.Memory;
(*$IFDEF Win32*)
Inc( PbmpBufferR, SizeOf(TBitmapFileHeader) );
(*$ELSE*)
Win16Inc( Pointer(PbmpBufferR), SizeOf(TBitmapFileHeader) );
(*$ENDIF*)
PbmpInfoR := PBitmapInfoHeader(PbmpBufferR);


{ Меняем ширину с высотой в информационном заголовке вращаемого изображения. }
with PbmpInfoR^ do begin
T := biHeight;
biHeight := biWidth;
biWidth := T;
biSizeImage :=  0 ;
end;


ABitmap := TBitmap.Create;


{ Вращение с самого начала. }
MemoryStreamR.Seek( 0 , soFromBeginning);
{ Загружаем это снова в ABitmap. }
ABitmap.LoadFromStream(MemoryStreamR);


MemoryStreamR.Free;
end;

procedure RotateBitmap180Degrees(var ABitmap: TBitmap);
var

RotatedBitmap: TBitmap;

begin

RotatedBitmap := TBitmap.Create;
with RotatedBitmap do begin
Width := ABitmap.Width;
Height := ABitmap.Height;
Canvas.StretchDraw( Rect(ABitmap.Width, ABitmap.Height,  0 ,  0 ), ABitmap );
end;
ABitmap.Free;
ABitmap := RotatedBitmap;
end;

end.  

...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32224765
Дмитрий Мыльников
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот это круто! :) И как ты, Andrew Campball, думаешь, сможет Арнис с твоими исходниками разобраться? :)

Я сам через них продирался с большим трудом, в том смысле, что для их понимания нужно знать ну очень много подробностей про BMP.

Теперь серьёзно. У меня есть примерно такой же модуль для обработки растра, который тоже писался на Delphi 2.0
По опыту написания и отладки того модуля могу предположить, что "шум на краях изображения скорее всего связан с неправильным учётом выравнивания битов в BMP (либо в Delphi 3 в настройках компилятора было включено что-то типа Aligned resord fields). Там есть ещё такой прикол от Microsoft, который заключается в том, что имеется некоторая неточность в описании формата BMP (по крайней мере в тех, что мне встречались) и выравнивание битов в растре не всегда до 8 бит (то есть до байта). В некоторых случаях изображение выравнивается до 16 бит (два байта).

А вообще, самое лучшее при работе с растрами вообще не использовать во внутренних операциях BMP формат, а сделать свою библиотечку с внутренним представлением растра, где всё несколько проще и понятнее устроено. И уже с этим внутренним предсталвением делать все необходимые трансформации. При этом нужно только написать пары функций по конвертации из/в разные форматы файлов (BMP, JPEG, TIFF и т.п.). Плюс вывод на экран, но это, опять же, через создание из внутреннего представления в BMP, причем делать BMP больше, чем размер отображаемого фрагмента на экране смысла нет.
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32224859
Фотография Andrew Campball
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот это круто! :) И как ты, Andrew Campball, думаешь, сможет Арнис с твоими исходниками разобраться? :)

Я сам через них продирался с большим трудом, в том смысле, что для их понимания нужно знать ну очень много подробностей про BMP.


Исходники не мои. Авторство указал.
К тому же человеку дали исходники, а уж его дело разобраться если необходимо. Я как говорится подкинул мысль.

Зачастую коллеги хотят получить готовое решение и голову не ломать.

К тому же я абсолютно не спец по графике.
...
Рейтинг: 0 / 0
Развернуть содержимое Image на 90гр.
    #32225247
Арнис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это вы хорошо завернули :) Вариант от Andrew Campball я сохранил, может как-нить удастся разобраться. Но тут случилось страшное - я нашёл компонент который вертает Jpeg на разный угол :) Так что - всем
Большое СПАСИБО . Да, если этот компонент кого заинтересует, то :
www.delphiarea.com
компонент RotateImage - поворачивает на любые углы. С исходниками.
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Развернуть содержимое Image на 90гр.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]